在WP7中解析iOS .plist文件

时间:2012-02-08 21:16:30

标签: ios xml parsing windows-phone-7 plist

我正在将iOS应用转换为WP7。我想要做的是使用我在WP7应用程序中为iOS应用程序创建的plist文件,而不对其进行任何更改。我尝试使用这里的库http://codetitans.codeplex.com/,但我只能解析一个级别,我的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>section0</key>
     <dict>
       <key>key0</key>
         <dict>
           <key>name</key>
           <string>Title</string>
           <key>type</key>
           <string>text</string>
           <key>filter</key>
           <false/>
         </dict>
       <key>key1</key>
         <dict>
           <key>name</key>
           <string>Season</string>
           <key>type</key>
           <string>text</string>            
           <key>filter</key>
           <false/>         
         </dict>
     </dict>
  </dict>

2 个答案:

答案 0 :(得分:4)

第1部分:您要求的内容

WP目前对动态类型没有很好的支持,所以解析它并不困难,消耗它会很难看。

该类将使用LINQ to XML解析PList:

public class PropertyListParser
{
    public IDictionary<String, Object> Parse(string plistXml)
    {
        return Parse(XDocument.Parse(plistXml));
    }

    public IDictionary<String, Object> Parse(XDocument document)
    {
        return ParseDictionary(document.Root.Elements("plist")
            .Elements("dict")
            .First());
    }

    private IDictionary<String, Object> ParseDictionary(XElement dictElement)
    {
        return dictElement
            .Elements("key")
            .ToDictionary(
                el => el.Value,
                el => ParseValue(el.ElementsAfterSelf("*").FirstOrDefault())
            );
    }

    private object ParseValue(XElement element)
    {
        if (element == null)
        {
            return null;
        }

        string valueType = element.Name.LocalName;

        switch (valueType)
        {
            case "string":
                return element.Value;
            case "dict":
                return ParseDictionary(element);
            case "true":
                return true;
            case "false":
                return false;
            default:
                throw new NotSupportedException("Plist element not supported: " + valueType);
        }
    }
}

以下是如何使用它的示例(基于您的示例):

var parsedPlist = new PlistParser().Parse(Plist);

var section0 = (IDictionary<string, object>)parsedPlist["section0"];

var key0 = (IDictionary<string, object>)parsedPlist["key0"];

string type = (string)key0["type"];
bool filter = (bool)key0["filter"];

第2部分:您可能需要的内容

话虽如此,实际上编写以这种方式使用它的代码会非常难看。根据您的架构,我会说以下实际上是您的应用程序所需要的。

// I'm not sure what your domain object is, so please rename this
public class ConfigEntry
{
    public string Name { get; set; }
    public string Type { get; set; }
    public bool Filter { get; set; }
}

public class ConfigEntryLoader
{
    private PropertyListParser plistParser;

    public ConfigEntryLoader()
    {
        plistParser = new PropertyListParser();
    }

    public ICollection<ConfigEntry> LoadEntriesFromPlist(string plistXml)
    {
        var parsedPlist = plistParser.Parse(plistXml);

        var section0 = (IDictionary<string, object>)parsedPlist["section0"];

        return section0.Values
            .Cast<IDictionary<string,object>>()
            .Select(CreateEntry)
            .ToList();
    }

    private ConfigEntry CreateEntry(IDictionary<string, object> entryDict)
    {
        // Accessing missing keys in a dictionary throws an exception,
        // so if they are optional you should check if they exist using ContainsKey
        return new ConfigEntry
        {
            Name = (string)entryDict["name"],
            Type = (string)entryDict["type"],
            Filter = (bool)entryDict["filter"]
        };
    }
}

现在,当您使用ConfigEntryLoader时,您将获得一个ConfigEntry对象列表,这将使您的代码更容易维护,以便传递字典。

ICollection<ConfigEntry> configEntries = new ConfigEntryLoader()
    .LoadEntriesFromPlist(plistXml);

答案 1 :(得分:1)

实际上CodeTitans Libraries解析所有级别。 这是我今天添加的单元测试,其中显示了您实际可以访问嵌套项目的方式:

[TestMethod]
public void LoadMultilevelItems()
{
    var input = @"<?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>section0</key> 
     <dict> 
       <key>key0</key> 
         <dict> 
           <key>name</key> 
           <string>Title</string> 
           <key>type</key> 
           <string>text</string> 
           <key>filter</key> 
           <false/> 
         </dict> 
       <key>key1</key> 
         <dict> 
           <key>name</key> 
           <string>Season</string> 
           <key>type</key> 
           <string>text</string>             
           <key>filter</key> 
           <false/>          
         </dict> 
     </dict> 
  </dict>
</plist>";

    var data = PropertyList.Read(input);

    Assert.IsNotNull(data);
    Assert.IsTrue(data.Contains("section0"));

    var section0 = data["section0"];
    Assert.IsNotNull(section0);
    Assert.IsTrue(section0.Contains("key0"));

    var key0 = section0["key0"];
    Assert.IsNotNull(key0);

    Assert.AreEqual("Title", key0["name"].StringValue);
    Assert.AreEqual("text", key0["type"].StringValue);
    Assert.IsFalse(key0["filter"].BooleanValue);
    key0.Add("filter", true);
}

默认情况下,所有项目都可以使用括号访问“词典”或“数组”。 当您下到该值时,只需使用专用属性'StringValue'或'BooleanValue',以避免在您自己的代码中进行类型转换。

还有更多的支持属性,例如:Type(允许您检查项目的本机plist类型)或ArrayItems和DictionaryItems(如果plist结构格式是动态的,则允许您枚举内容)。