65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中简单的 PList 解析器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2012年6月19日

CPOL

2分钟阅读

viewsIcon

49318

一个用 100 行 C# 代码实现的简单 plist 解析器。

引言

最近我负责在工作中开发一个纹理打包工具。我们使用纯 C# (4.0) 来编写我们的工具。我需要在我的代码中读取和解析 plist 文件。我在互联网上搜索过,但不幸的是,似乎缺少 C# 中的 plist 解析器。通过阅读几个示例文件,我很快就理解了 plist 语法,因为它对于人类来说很容易分析,对于解析器来说也很容易识别。我决定重新造这个轮子。

直接看代码

实现非常简单,我大约在一个小时内完成了它。让我们快速看一下整个代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace Data
{
    public class PList : Dictionary<string, dynamic>
    {
        public PList()
        {
        }

        public PList(string file)
        {
            Load(file);
        }

        public void Load(string file)
        {
            Clear();

            XDocument doc = XDocument.Load(file);
            XElement plist = doc.Element("plist");
            XElement dict = plist.Element("dict");

            var dictElements = dict.Elements();
            Parse(this, dictElements);
        }

        private void Parse(PList dict, IEnumerable<XElement> elements)
        {
            for (int i = 0; i < elements.Count(); i += 2)
            {
                XElement key = elements.ElementAt(i);
                XElement val = elements.ElementAt(i + 1);

                dict[key.Value] = ParseValue(val);
            }
        }

        private List<dynamic> ParseArray(IEnumerable<XElement> elements)
        {
            List<dynamic> list = new List<dynamic>();
            foreach (XElement e in elements)
            {
                dynamic one = ParseValue(e);
                list.Add(one);
            }

            return list;
        }

        private dynamic ParseValue(XElement val)
        {
            switch (val.Name.ToString())
            {
                case "string":
                    return val.Value;
                case "integer":
                    return int.Parse(val.Value);
                case "real":
                    return float.Parse(val.Value);
                case "true":
                    return true;
                case "false":
                    return false;
                case "dict":
                    PList plist = new PList();
                    Parse(plist, val.Elements());
                    return plist;
                case "array":
                    List<dynamic> list = ParseArray(val.Elements());
                    return list;
                default:
                    throw new ArgumentException("Unsupported");
            }
        }
    }
}

是不是很清晰?

LINQ 和 dynamic 类型使这个轻量级类受益匪浅。如果没有它们,就需要更多的代码行。这个类是一个递归数据结构,虽然我们看不到其中定义了 PList 属性;它的基类是 Dictionary<string, dynamic>,其中的值可以是 string/int/float/bool/PList/List<dynamic> 的值。解析方法是递归的,因为其结构是递归的。Parse 方法枚举 XML 数据中的所有键值对,并将解析后的字符串-动态对填充到 PList 结构中;每一步它都会调用 ParseValue 方法来解析一个值;ParseValue 方法根据 XML 节点名称中指示的类型,调用数据转换方法或 ParseArrayParse 递归地调用。ParseArray 方法枚举所有子 XML 节点,并通过递归调用 ParseValue 将它们解析为动态类型。当然,我们可以使用 object 代替 dynamic,但我们必须在从 PList 中检索值时添加更多的类型区分,这是我希望避免的。

使用代码

要解析 plist 文件,只需编写如下代码:

PList plist = new PList();
plist.Load("file_path.plist");

PList plist = new PList("file_path.plist");

检索解析后的数据与使用泛型 Dictionary 相同,因为 PList 派生自 Dictionary<string, dynamic>。代码可能看起来粗糙,因为我只想在这里展示主要思想,而不想加入干扰信息。我认为您应该并且应该在使用之前对其进行完善,例如添加一些错误/异常处理,或者添加一个序列化方法等。这些可以是非常简单的任务。

玩得开心!

用 C# 编写 plist 解析器比我预期的更容易,并且对我来说效果很好。我希望它也能帮助你。

© . All rights reserved.