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

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

  • 相关阅读:
    JavaWeb——Servlet开发3
    8.3.3
    8.3.2
    8.3
    8.2
    8.1
    7.3.9
    7.3.8.3
    7.3.8.2
    7.3.8.1
  • 原文地址:https://www.cnblogs.com/jacklandrin/p/2908968.html
Copyright © 2011-2022 走看看