C# 读取Apple PList文件 C#如何解析macOS和iOS使用的XML或二进制属性列表

1次阅读

plistcs是c#中读取xml和二进制plist最省事的开源库,轻量、兼容性好,支持.net framework 4.6+及.net 5/6/7/8,自动识别bplist00等文件头。

C# 读取Apple PList文件 C#如何解析macOS和iOS使用的XML或二进制属性列表

PlistCS 读取 XML 和二进制 PList 最省事

直接上结论:C# 没有原生 PList 支持,PlistCS 是目前最轻量、兼容性最好、能同时处理 XML 和二进制格式的开源库。它不依赖 macOS 或 .NET Core 特定运行时,.NET Framework 4.6+ 和 .NET 5/6/7/8 都能跑。

常见错误是硬啃 apple 官方文档自己解析二进制格式——NSKeyedArchiver 生成的不是标准 PList,而 CFBinaryPlist 格式又没公开完整规范,极易在字节对齐、浮点编码、日期偏移上翻车。

  • 安装:dotnet add package PlistCS(NuGet)
  • 它自动识别文件头:bplist00 → 二进制;<?xml /code> → XML
  • 不支持 NSKeyedArchiver 序列化产物(那种带 $class$classes 的),那是另一套序列化机制,不是 PList

PlistParser.Parse 怎么用才不崩

别直接传路径字符串进去——PlistParser.Parse 接收的是 Streambyte[],传错类型会抛 NullReferenceException 或静默返回空字典。

典型场景:读取 macosInfo.plistios 导出的 Manifest.plist。这些文件可能带 BOM、换行不一致,但 PlistParser.Parse 内部已处理,你只需确保流可读且未被释放。

  • 正确写法:var dict = PlistParser.Parse(File.OpenRead("Info.plist"));
  • 错误写法:PlistParser.Parse("Info.plist")(编译不过)或 PlistParser.Parse(File.ReadAllText(...))(类型不匹配)
  • 返回值是 IDictionary<string object></string>,嵌套结构里 object 可能是 stringDateTimelongboolIDictionary<string object></string>IList<object></object>,需运行时判断

遇到 Invalid binary plist header 错误怎么办

这通常不是文件损坏,而是你把非 PList 文件当 PList 读了——比如误读了 embedded.mobileprovision(那是 PKCS#7 签名数据)或 AppIcon20x20@2x.png(图片),它们开头也可能是乱码,但根本不是 PList。

更隐蔽的情况:某些工具导出的“PList”其实是 JSON 伪装的(比如部分配置管理平台),或者用了 UTF-16 编码但没写 BOM,XML 解析器会卡在第一个字符。

  • 先用命令行确认:file Info.plistmacOS/Linux)或看十六进制头:bplist00(二进制)、3C 3F 78 6D 6C
  • 如果头是 6A 73 6F 6E(json),别用 PlistParser,换 System.Text.Json
  • 二进制 PList 若含自定义类型(如 CFTypeRef 子类),PlistCS 会跳过并记录警告,不会崩溃,但对应 key 的 value 为 null

IDictionary<string object></string> 安全取值的坑

很多人写 dict["CFBundleIdentifier"].ToString() 就跑,结果遇到 key 不存在、值为 null、或值其实是 NSNumber 映射的 long 却强转 string,直接 NullReferenceException 或格式错乱。

特别是 CFBundleVersion 可能是整数(iOS 早期)或字符串(现代),UIBackgroundModes 是数组,NSLocationWhenInUseUsageDescription 是字符串——类型不统一是常态。

  • 安全取字符串:dict.TryGetValue("CFBundleIdentifier", out var idObj) && idObj is string id ? id : null
  • 取整数版本号:dict.TryGetValue("CFBundleVersion", out var vObj) && int.TryParse(vObj?.ToString(), out var ver) ? ver : 0
  • 取数组并遍历:if (dict.TryGetValue("UIBackgroundModes", out var modesObj) && modesObj is IList<object> modes)</object>

二进制 PList 里的日期会被自动转成 DateTime(UTC),但 XML PList 里是 ISO 8601 字符串,PlistCS 统一做了转换,这点不用额外处理——但如果你手动改过源码或用了旧版,就得自己 parse。

text=ZqhQzanResources