【问题标题】:Parse Xml with same key values使用相同的键值解析 Xml
【发布时间】:2014-07-19 21:09:40
【问题描述】:

我正在开发 Windows Phone 8 应用程序,

我有一些看起来像这样的用户界面:

主项 A --- 将其描述和子项列表作为键值

主项 B --- 有它的 desc 和子项列表作为键值

主项 C --- 有它的描述和子项列表作为键值

现在点击 A 移动到下一页,该页面将显示其描述及其子项目。

点击主条目 A

主要项目A的描述

sub item 1 --- 点击这个显示它的描述 子项 2 --- 单击此显示其说明

下面是 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);
        }

在上面我能得到除了except MainTitle Description之外的所有东西,你能帮我纠正一下吗。

【问题讨论】:

    标签: c# wpf windows-phone-8 xml-parsing linq-to-xml


    【解决方案1】:

    您试图在一个只有两个项目(键和值)空间的数据模型中压缩 3 条信息(名称、描述和子项目列表)。

    我提供了两种解决方案。一个“修复”代码中的问题,一个是更灵活的解决方案。选择你喜欢的一个

    快速修复

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

    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 的类,它包含一组菜单项,而这些菜单项又可以包含一组菜单项,我使用以下 PlistParser 类返回提到的类模型。

    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;
        }
    
    }
    

    类模型

    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");
    

    【讨论】:

    • 所以我所说的菜单现在在你的例子中被称为技巧。 MainTitle 是什么/在哪里?
    • 不不,我的意思是你能帮我解决我的灵魂问题吗?
    • 这里:NameADescription一些数据我需要A的描述
    • @user3114009 我为您的问题添加了修复程序。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 1970-01-01
    • 2014-06-18
    • 2021-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多