用相同的键值解析Xml [英] Parse Xml with same key values

查看:52
本文介绍了用相同的键值解析Xml的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Windows Phone 8应用程序,

I am working on Windows Phone 8 application,

我有一些看起来像这样的UI:

I have some UI which looks like this :

主要项目A ---具有其desc和子项目列表作为键值

Main Item A --- has its desc and list of subitems as key value

主要项目B ---具有其desc和子项目列表作为键值

Main Item B --- has its desc and list of subitems as key value

主要项目C ---具有其desc和子项目列表作为键值

Main Item C --- has its desc and list of subitems as key value

现在,单击A移至下一页,将显示其说明及其子项.

Now on click of A move to next page which will display its Description and its sub items.

点击主条目A

主要项目A的描述

子项目1 ---单击此显示时其描述子项目2 ---单击此显示时,其说明

sub item 1 --- On click of this display its desc sub item 2 --- On click of this display its desc

这是Xml的样子:

<plist version="1.0">

    <dict>
        <key>Category</key>
        <array>
            <dict>
                <key>Name</key>
                <string>A</string>
                <key>Description</key>
                <string>Some data</string>
                <key>SubItems</key>
                <array>
                    <dict>
                        <key>Description</key>
                        <string>Some data</string>
                        <key>Name</key>
                        <string>One</string>
                    </dict>
                    <dict>
                        <key>Name</key>
                        <string>Two</string>
                        <key>Description</key>
                        <string>Some data</string>
                    </dict>

                </array>
            </dict>
            <dict>
                <key>Name</key>
                <string>B</string>
                <key>Description</key>
                <string>Some data</string>
                <key>SubItems</key>
                <array>
                    <dict>
                        <key>Description</key>
                        <string>Some data</string>
                        <key>Name</key>
                        <string>One</string>
                    </dict>
                    <dict>
                        <key>Name</key>
                        <string>Two</string>
                        <key>Description</key>
                        <string>Some data</string>
                    </dict>

                </array>
            </dict>

如何解析此示例?

更新

我已经这样解决了:

Dictionary<string, List<Tricks>> plistData =
                    doc.Root.Element("dict").Element("array").Elements("dict")
                        .Select(GetValues)
                        .ToDictionary(v => (string)v["Name"],
                                      v => v["SubItems"]
                                      .Elements("dict").Select(Parse).ToList());

static Tricks Parse(XElement dict)
        {
            var values = GetValues(dict);

            return new Tricks
            {
                SubTitle = (string)values["Name"],
                SubTitleDescription = (string)values["Description"]
            };
        }

static Dictionary<string, XElement> GetValues(XElement dict)
        {
            return dict.Elements("key")
                       .ToDictionary(k => (string)k, k => (XElement)k.NextNode);
        }

在上面,我可以得到除MainTitle Description之外的所有其他代码,请您帮我改正.

In the above i am able to get everything but except MainTitle Description, can you please help me to correct it.

推荐答案

您正试图在一个只能容纳两个项目(a的空间)的数据模型中压缩3条信息(名称,描述和子项目列表)键和一个值).

You are trying to squeeze 3 pieces of information (a Name, a Description and a list of subitems) in an datamodel that only has room for two items (a key and a value).

我提供两种解决方案.一种是解决"代码中的问题,另一种是更灵活的解决方案.选一个你喜欢的人

I provide two solutions. One that 'fixes' the problem in your code and one that is a more flexible solution. Pick either one you like

最大的变化是字典不再返回字符串,而是完整的 Tricks 对象.

The biggest change is that the dictionary no longer returns a string but a fullfledge Tricks object.

public Dictionary<Tricks, List<Tricks>> Clumsy(XDocument doc)
{
    var plistData =
        doc
            .Root
            .Element("dict")
            .Element("array")
            .Elements("dict")
            .Select( ele => new     
                {
                    key = Parse(ele),
                    val = ele.Element("array")
                           .Elements("dict")
                           .Select(Parse).ToList()
                }).ToDictionary(pair => pair.key,
                                pair => pair.val);
    return plistData;
}

static Tricks Parse(XElement dict)
{
    var values = GetValues(dict);

    return new Tricks
    {
        SubTitle = (string)values["Name"],
        SubTitleDescription = (string)values["Description"]
    };
}

static Dictionary<string, XElement> GetValues(XElement dict)
{
    return dict.Elements("key")
               .ToDictionary(k => (string)k, k => (XElement)k.NextNode);
}

更灵活的解决方案

假设您像MenuRoot类那样冒充,该类包含Menu项目的集合,而这些Menu项目又可以容纳Menu项目的集合,我使用了以下 PlistParser 类来返回上述的类模型./p>

More flexible solution

Assuming you have soming like a MenuRoot class which holds a collection of Menu items that in turn can also hold a collecion of Menu items I used the following PlistParser class to return th mentioned class model.

public class PListParser
{
    public T Deserialize<T>(Stream stream) where T : new()
    {
        return Deserialize<T>(XDocument.Load(stream));
    }

    public T Deserialize<T>(string xml) where T:new()
    {
        return Deserialize<T>(XDocument.Parse(xml));
    }

    private T Deserialize<T>(XDocument doc) where T : new()
    {
        return DeserializeObject<T>(
            doc.Document.
            Element("plist").
            Element("dict"));
    }

    // parse th xml for an object
    private T DeserializeObject<T>(XElement dict) where T:new()
    {
        var obj = new T();
        var objType = typeof (T);

        // get either propertty names or XmlElement values
        var map = GetMapping(objType);

        // iterate over the key elements and match them against
        // the names of the properties of ther class
        foreach (var key in dict.Elements("key"))
        {
            var pi = map[key.Value];
            if (pi != null)
            {
                // the next node is the value
                var value = key.NextNode as XElement;
                if (value != null)
                {
                    // what is the type of that value
                    switch (value.Name.ToString())
                    {
                        case "array":
                            // assume a generic List for arrays
                            // process subelements
                            object subitems = InvokeDeserializeArray(
                                pi.PropertyType.GetGenericArguments()[0],
                                value);
                            pi.SetValue(obj, subitems, null);
                            break;
                        case "string":
                            // simple assignment
                            pi.SetValue(obj, value.Value, null);
                            break;
                        case "integer":
                            int valInt;
                            if (Int32.TryParse(value.Value, out valInt))
                            {
                                pi.SetValue(obj, valInt, null);
                            }
                            break;
                        default:
                            throw new NotImplementedException(value.Name.ToString());
                            break;
                    }
                }
                else
                {
                    Debug.WriteLine("value null");
                }
            }
            else
            {
                Debug.WriteLine(key.Value);
            }
        }
        return obj;
    }

    // map a name to a properyinfo
    private static Dictionary<string, PropertyInfo> GetMapping(Type objType)
    {
        // TODO: Cache..
        var map = new Dictionary<string, PropertyInfo>();
        // iterate over all properties to find...
        foreach (var propertyInfo in objType.GetProperties())
        {
            // .. if it has an XmlElementAttribute on it
            var eleAttr = propertyInfo.GetCustomAttributes(
                typeof (XmlElementAttribute), false);
            string key;
            if (eleAttr.Length == 0)
            {
                // ... if it doesn't the property name is our key
                key = propertyInfo.Name;
            }
            else
            {
                // ... if it does the ElementName given the attribute 
                // is the key.
                var attr = (XmlElementAttribute) eleAttr[0];
                key = attr.ElementName;
            }
            map.Add(key, propertyInfo);
        }
        return map;
    }

    //http://stackoverflow.com/a/232621/578411
    private object InvokeDeserializeArray(Type type, XElement value)
    {
        MethodInfo method = typeof(PListParser).GetMethod(
            "DeserializeArray",
            BindingFlags.Instance | 
            BindingFlags.InvokeMethod | 
            BindingFlags.NonPublic);
        MethodInfo generic = method.MakeGenericMethod(type);
        return generic.Invoke(this, new object[] {value});
    }

    // array handling, returns a list
    private List<T> DeserializeArray<T>(XElement array) where T:new()
    {
        var items = new List<T>();
        foreach (var dict in array.Elements("dict"))
        {
            items.Add(DeserializeObject<T>(dict));
        }
        return items;
    }

}

Classmodel

public class MenuRoot
{
    public List<Tricks> Category { get; set; }
}

public class Tricks
{
    [XmlElementAttribute("Name")]
    public string SubTitle { get; set; }
    [XmlElementAttribute("Description")]
    public string SubTitleDescription { get; set; }
    public List<Tricks> SubItems { get; set; }
}

用法

var parser = new PListParser();
var menu =  parser.Deserialize<MenuRoot>(@"c:\my\path\to\the\plist.xml");

这篇关于用相同的键值解析Xml的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆