使用C#反序列化plist文件
因为我们的项目是跨平台的,最初做的是ios版,所以打包的xml都是plist格式的。plist在xcode里反序列化比较容易,而PC版里最初是依照不同文件的结构,做得不同解析,代码复用性比较低。
我接手这个项目之后,老大让我再加一个文件的解析,看了一下之前的那个代码感觉比较乱,干脆就自己直接写了一个plist文件的反序列化算法,在这里和大家分享一下。
我的思路是这样的,先将plist文件中的元素用树的形式储存起来,再将树组织成一个标准的XML文件,利用C#中的XmlSerializer将其反序列化。
若要解析plist文件,我们首先要了解其文件的格式,下面是一个plist文件
<? xml version="1.0" encoding="UTF-8"?> <! DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> < plist version="1.0"> < dict > < key >CFBundleDevelopmentRegion</ key > < string >zh_CN</ string > < key >CFBundleDisplayName</ key > < string >${PRODUCT_NAME}</ string > < key >CFBundleExecutable</ key > < string >${EXECUTABLE_NAME}</ string > < key >CFBundleIconFiles</ key > < array > < string >Icon.png</ string > < string >Icon@2x.png</ string > </ array > < key >CFBundleIcons</ key > < dict > < key >CFBundlePrimaryIcon</ key > < dict > < key >CFBundleIconFiles</ key > < array > < string >Icon.png</ string > < string >Icon@2x.png</ string > </ array > < key >UIPrerenderedIcon</ key > < false /> </ dict > </ dict > < key >CFBundleIdentifier</ key > < string >com.qixiangWeather.${PRODUCT_NAME:rfc1034identifier}</ string > < key >CFBundleInfoDictionaryVersion</ key > < string >6.0</ string > < key >CFBundleName</ key > < string >${PRODUCT_NAME}</ string > < key >CFBundlePackageType</ key > < string >APPL</ string > < key >CFBundleShortVersionString</ key > < string >1.0</ string > < key >CFBundleSignature</ key > < string >????</ string > < key >CFBundleVersion</ key > < string >1.0</ string > < key >LSRequiresIPhoneOS</ key > < true /> < key >UIRequiredDeviceCapabilities</ key > < array > < string >armv7</ string > </ array > < key >UISupportedInterfaceOrientations</ key > < array > < string >UIInterfaceOrientationPortrait</ string > < string >UIInterfaceOrientationLandscapeRight</ string > < string >UIInterfaceOrientationLandscapeLeft</ string > </ array > </ dict > </ plist > |
我们可以看出它和标准的xml文件有很多不同,它具有以下几种标签格式来代表不同类型的数据:
Core Fundation XML
CFString <string>
CFNumber <real> 或 <integer>
CFDate <date>
CFBoolean <true/> 或 <false/>
CFData <data>
CFArray <array>
CFDictionary <dict>
在CFDictionary中数据主要由键值对组成。因此在XML中,CFDictioary成员的键对应为<key>,之后便是它相应的值。
根据这一特点我们可以枚举出键值类型
enum EnumValueType { DICT, ARRAY, NUMBER, STRING, DTAE, BOOLEAN, DATA, }
现在我们要把plist格式转化成树,定义一下数据结构
class DataType { public int ID { get; set; }//元素ID public string DataName { get; set; }//元素数据名称 public EnumValueType ValueType { get; set; }//元素值类型 public string Value { get; set; }//元素值 public int parentID { get; set; }//元素父节点ID public List<int> childrenID { get; set; }//元素子节点 }
解析的算法是一个递归的过程,这里我们用一个list将树存起来,如果你疑惑为什么list里的ID不是连续的,我可以提醒你一下遍历的过程是一个前序遍历
class ReadPlist { public List<DataType> DateList { get; set; } int saveid = 1; DataType RootNode; int count; int itemCount; public ReadPlist() { count = 1; itemCount = 0; RootNode = new DataType(); RootNode.ID = count; RootNode.DataName = null; RootNode.parentID = 0; RootNode.ValueType = EnumValueType.DICT; RootNode.childrenID = new List<int>(); this.DateList = new List<DataType>(); DateList.Add(RootNode); } public XDocument LoadFromFile(string path) { return XDocument.Load(path); } public void XMLparser(string path) { XDocument doc = LoadFromFile(path); XElement FirstElement = doc.Root.Element("dict"); DateList[0].childrenID = XMLOnce(FirstElement, 1); foreach (var item in DateList) { if (item.Value == "FALSE"||item.Value == "TRUE") { item.Value = item.Value.ToLower(); } try { if (Char.IsNumber(item.DataName[0])) { item.DataName.Insert(0, "_"); } } catch (System.Exception ex) { } } print(); } public List<int> XMLOnce(XElement nowElement,int parentid) { List<DataType> DataTemp = new List<DataType>(); List<int> IDList = new List<int>(); List<int> childrenIDList = new List<int>(); var keys = from k in nowElement.Elements("key") select k; var values = from v in nowElement.Elements() where v.Name != "key" select v ; for (int i = 0; i < values.ToList().Count; i++) { int id = ++count; EnumValueType valuetype = (EnumValueType)Enum.Parse(typeof(EnumValueType), values.ToList()[i].Name.LocalName.ToString().ToUpper(), true); string value = null; if (valuetype == EnumValueType.ARRAY) { XElement newElement = nowElement.Elements().Except(nowElement.Elements("key")).ElementAt(i); int num = newElement.Elements().Count(); for (int j = 0; j < num;j++ ) { newElement.AddFirst(new XElement("key", "item")); } childrenIDList = XMLOnce(newElement,id); } else if (valuetype == EnumValueType.DICT) { XElement newElement = nowElement.Elements().Except(nowElement.Elements("key")).ElementAt(i); childrenIDList = XMLOnce(newElement, id); } else { value = values.ToList()[i].Value.ToString(); } try { DataTemp.Add(new DataType() { DataName = keys.ToList()[i].Value.ToString(), ValueType = valuetype, ID = id, Value = value, parentID = parentid, childrenID = childrenIDList }); } catch (System.Exception ex) { DataTemp.Add(new DataType() { DataName = "itemContent", ValueType = valuetype, ID = id, Value = value, parentID = parentid, childrenID = childrenIDList }); } } foreach (var item in DataTemp) { IDList.Add(item.ID); } DateList.AddRange(DataTemp); return IDList; }
}
好的,我们下一步是把这个树写成一个标准的xml格式,我这里对xml的解析用的是linq to xml,如果有不熟悉linq的朋友,可以翻阅园子里的其他文章学习一下
class ConvertXML { List<DataType> DataList{get;set;} public ConvertXML (List<DataType> datalist) { DataList = datalist; } public XDocument xdoc = new XDocument(new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("Model"))); public XDocument creatXML() { xdoc.Element("Model").SetAttributeValue("id","1"); foreach (var item in DataList) { if (DataList[0].childrenID.Contains(item.ID)) { //xdoc.Element("Model").Add(new XElement(item.DataName, item.Value)); XElement newElement = xdoc.Descendants().Where(e => e.Attribute("id").Value == "1").First(); newElement.Add(new XElement(item.DataName, item.Value, new XAttribute("id", item.ID))); if (item.ValueType == EnumValueType.ARRAY||item.ValueType == EnumValueType.DICT) { //XElement newElement = xdoc.Element("Model").Element(item.DataName); creatOnce(newElement, item); } } } return xdoc; } public void creatOnce(XElement doc,DataType parent) { foreach (var item in DataList) { if (parent.childrenID.Contains(item.ID)) { string parentID = parent.ID.ToString(); XElement newElement = doc.Descendants().Where(e => e.Attribute("id").Value.Equals(parentID)).First(); newElement.Add(new XElement(item.DataName, item.Value, new XAttribute("id", item.ID))); if (item.ValueType == EnumValueType.ARRAY||item.ValueType == EnumValueType.DICT) { //nowElement = nowElement.Element(item.DataName); creatOnce(newElement,item); } } } } }
剩下最后一步,就是对现在标准的xml进行反序列化了,我们对外只留文件路径和类的程序集名称
class PlistSerializer : XmlSerializer { public object Deserialize(string path,string assemblyName) { MemoryStream stream = new MemoryStream(); ReadPlist rp = new ReadPlist(); rp.XMLparser(path); ConvertXML cx = new ConvertXML(rp.DateList); //Console.WriteLine(cx.creatXML()); XDocument doc = cx.creatXML(); doc.Save(stream); stream.Seek(0, SeekOrigin.Begin); Type modelType = Type.GetType(assemblyName); XmlSerializer serializer = new XmlSerializer(modelType); try { object sender = serializer.Deserialize(stream); stream.Close(); return sender; } catch (Exception e) { Console.WriteLine(e.Message); stream.Close(); return null; } } }
这样用C#对plist文件的反序列化就大功告成了,代码比较简单,大家还可以根据需要,加上序列化的功能。
本代码codeplex:https://plistparser.codeplex.com/SourceControl/changeset/view/901458f7e974
如果大家有对plist序列化和反序列化的需求,感觉我的代码不够优化或者功能不够强大,也可以利用网上的第三方库来实现功能,比如iphone-plist-net
感谢阅读倾剑飞血的博客
本文地址:http://www.cnblogs.com/jacklandrin/archive/2013/02/07/2908968.html
本文版权归作者所有,欢迎转载,演绎或用于商业目的,但是必须说明本文出处(包含链接)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?