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

用 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 接收的是 Stream 或 byte[],传错类型会抛 NullReferenceException 或静默返回空字典。
典型场景:读取 macos 的 Info.plist 或 ios 导出的 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可能是string、DateTime、long、bool、IDictionary<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.plist(macOS/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。