zoukankan      html  css  js  c++  java
  • 使用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

    本文版权归作者所有,欢迎转载,演绎或用于商业目的,但是必须说明本文出处(包含链接)。

  • 相关阅读:
    Java实现 LeetCode 343 整数拆分(动态规划入门经典)
    Java实现 LeetCode 342 4的幂
    Java实现 LeetCode 342 4的幂
    Java实现 LeetCode 342 4的幂
    Java实现 LeetCode 341 扁平化嵌套列表迭代器
    Java实现 LeetCode 341 扁平化嵌套列表迭代器
    Java实现 LeetCode 341 扁平化嵌套列表迭代器
    Java实现 LeetCode 338 比特位计数
    H264(NAL简介与I帧判断)
    分享一段H264视频和AAC音频的RTP封包代码
  • 原文地址:https://www.cnblogs.com/jacklandrin/p/2908968.html
Copyright © 2011-2022 走看看