在跨平台开发或者需要兼容macOS应用配置的场景中,使用C#操作.plist属性列表文件是常见需求。.plist文件是macOS系统用于存储结构化数据的文件格式,主要分为XML格式和二进制格式两种,存储的内容包含字符串、数字、布尔值、数组、字典等多种数据类型。

plist文件格式基础
XML格式的.plist文件有固定的文档结构,根节点为<plist>,内部主要包含<dict>字典、<array>数组、<string>字符串、<integer>整数、<real>浮点数、<true/>和<false/>布尔值等标签。二进制格式的.plist文件则无法直接阅读,需要通过专门的解析逻辑处理。
使用第三方库操作plist文件
目前C#生态中比较成熟的plist处理库是plist-cil,它支持XML和二进制两种格式的plist文件读写,使用起来比较便捷。首先需要通过NuGet安装该库,安装命令如下:
dotnet add package plist-cil
读取plist文件
以下是读取XML格式plist文件的示例代码,假设我们有一个存储应用配置的config.plist文件:
using System;
using Plist;
namespace PlistDemo
{
class Program
{
static void Main(string[] args)
{
// 读取plist文件路径
string plistPath = "config.plist";
// 加载plist文件
PlistDocument plist = PlistDocument.Load(plistPath);
// 获取根节点字典
PlistDictionary rootDict = plist.Root as PlistDictionary;
if (rootDict != null)
{
// 读取字符串类型的值
if (rootDict.ContainsKey("AppName"))
{
string appName = rootDict["AppName"].AsString();
Console.WriteLine($"应用名称:{appName}");
}
// 读取整数类型的值
if (rootDict.ContainsKey("VersionCode"))
{
int versionCode = rootDict["VersionCode"].AsInteger();
Console.WriteLine($"版本号:{versionCode}");
}
// 读取布尔类型的值
if (rootDict.ContainsKey("IsDebug"))
{
bool isDebug = rootDict["IsDebug"].AsBool();
Console.WriteLine($"是否调试模式:{isDebug}");
}
// 读取数组类型的值
if (rootDict.ContainsKey("SupportLanguages"))
{
PlistArray langArray = rootDict["SupportLanguages"] as PlistArray;
if (langArray != null)
{
Console.WriteLine("支持的语言:");
foreach (var item in langArray)
{
Console.WriteLine(item.AsString());
}
}
}
}
}
}
}
如果是二进制格式的plist文件,上述代码同样适用,plist-cil库会自动识别文件格式并进行解析。
写入plist文件
创建并写入plist文件的代码示例如下,我们可以构建一个包含多种数据类型的plist文件:
using System;
using Plist;
namespace PlistDemo
{
class Program
{
static void Main(string[] args)
{
// 创建plist文档
PlistDocument plist = new PlistDocument();
// 创建根字典
PlistDictionary rootDict = new PlistDictionary();
// 添加字符串类型数据
rootDict["AppName"] = new PlistString("TestApp");
// 添加整数类型数据
rootDict["VersionCode"] = new PlistInteger(1001);
// 添加布尔类型数据
rootDict["IsDebug"] = new PlistBool(true);
// 添加浮点数类型数据
rootDict["VersionFloat"] = new PlistReal(1.2);
// 添加数组类型数据
PlistArray langArray = new PlistArray();
langArray.Add(new PlistString("中文"));
langArray.Add(new PlistString("英文"));
langArray.Add(new PlistString("日文"));
rootDict["SupportLanguages"] = langArray;
// 设置根节点
plist.Root = rootDict;
// 保存为XML格式plist文件
plist.Save("new_config.plist", PlistFormat.Xml);
// 也可以保存为二进制格式
// plist.Save("new_config_binary.plist", PlistFormat.Binary);
Console.WriteLine("plist文件写入完成");
}
}
}
手动解析XML格式plist文件
如果不想引入第三方库,也可以手动解析XML格式的plist文件,这种方式仅适用于XML格式的plist,无法处理二进制格式。核心思路是使用System.Xml命名空间下的XML解析类读取节点内容,再按照plist的标签规则转换数据类型。
以下是手动解析XML plist的示例代码:
using System;
using System.Collections.Generic;
using System.Xml;
namespace PlistDemo
{
class Program
{
static void Main(string[] args)
{
string plistPath = "config.plist";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(plistPath);
// 获取plist根节点
XmlNode plistNode = xmlDoc.SelectSingleNode("plist");
if (plistNode != null)
{
// 获取dict子节点
XmlNode dictNode = plistNode.SelectSingleNode("dict");
if (dictNode != null)
{
Dictionary<string, object> resultDict = ParseDictNode(dictNode);
// 输出解析结果
foreach (var item in resultDict)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
}
}
}
static Dictionary<string, object> ParseDictNode(XmlNode dictNode)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
XmlNodeList childNodes = dictNode.ChildNodes;
for (int i = 0; i < childNodes.Count; i++)
{
XmlNode node = childNodes[i];
if (node.Name == "key")
{
string key = node.InnerText;
// 下一个节点是对应的值节点
if (i + 1 < childNodes.Count)
{
XmlNode valueNode = childNodes[i + 1];
object value = ParseValueNode(valueNode);
dict[key] = value;
i++; // 跳过值节点
}
}
}
return dict;
}
static object ParseValueNode(XmlNode valueNode)
{
switch (valueNode.Name)
{
case "string":
return valueNode.InnerText;
case "integer":
return int.Parse(valueNode.InnerText);
case "real":
return double.Parse(valueNode.InnerText);
case "true":
return true;
case "false":
return false;
case "array":
List<object> array = new List<object>();
foreach (XmlNode child in valueNode.ChildNodes)
{
array.Add(ParseValueNode(child));
}
return array;
case "dict":
return ParseDictNode(valueNode);
default:
return null;
}
}
}
}
操作注意事项
- 处理plist文件时注意编码问题,XML格式的plist文件通常使用UTF-8编码,读取时避免编码错误导致内容乱码。
- 二进制格式的plist文件结构更复杂,手动解析难度较高,建议优先使用成熟的第三方库处理。
- 写入plist文件时如果需要兼容macOS系统的读取,建议优先选择XML格式,兼容性更好。
- 解析plist的字典节点时,要注意key和值节点的对应关系,避免索引错误。