0

我正在尝试反序列化从 Moodle Web 服务收到的 XML 响应。

如果它具有不同的命名属性,例如 id、shortname、idnumber 等,我可以将其解析为 dotnet 对象。但它有一个 KEY 属性数组,其中实际字段名称作为值,并且在其中,还有另一个节点具有字段值.

这是一个示例:

    <?xml version="1.0" encoding="UTF-8" ?>
        <RESPONSE>
            <MULTIPLE>
            <SINGLE>
                <KEY name="id">
                    <VALUE>2</VALUE>
                </KEY>
                <KEY name="shortname">
                    <VALUE>CS-101</VALUE>
                </KEY>
                <KEY name="fullname">
                    <VALUE>CS-101</VALUE>
                </KEY>
                <KEY name="enrolledusercount">
                    <VALUE>2</VALUE>
                </KEY>
                <KEY name="idnumber">
                    <VALUE></VALUE>
                </KEY>
                <KEY name="visible">
                    <VALUE>1</VALUE>
                </KEY>
                <KEY name="summary">
                    <VALUE>&lt;p&gt;CS-101&lt;br /&gt;&lt;/p&gt;</VALUE>
                </KEY>
                <KEY name="summaryformat">
                    <VALUE>1</VALUE>
                </KEY>
                <KEY name="format">
                    <VALUE>weeks</VALUE>
                </KEY>
                <KEY name="showgrades">
                    <VALUE>1</VALUE>
                </KEY>
                <KEY name="lang">
                    <VALUE></VALUE>
                </KEY>
                <KEY name="enablecompletion">
                    <VALUE>0</VALUE>
                </KEY>
            </SINGLE>
            </MULTIPLE>
        </RESPONSE>

我想将此 XML 解析为此类的对象:

class Course
{
    public int id { get; set; }
    public string shortname { get; set; }  //short name of course
    public string fullname { get; set; }   //long name of course
    public int enrolledusercount { get; set; }  //Number of enrolled users in this course
    public string idnumber { get; set; }   //id number of course
    public int visible { get; set; }  //1 means visible, 0 means hidden course
    public string summary { get; set; }
    public int summaryformat { get; set; } //summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN)
    public string format { get; set; } //course format: weeks, topics, social, site
    public int showgrades { get; set; } //true if grades are shown, otherwise false
    public string lang { get; set; } //forced course language
    public int enablecompletion { get; set; } //true if completion is enabled, otherwise false
}

有没有直接的方法,或者我应该为每个字段编写一个带有 switch case 的解析器方法?

4

3 回答 3

2

您需要使用 XmlReader 编写自定义解析器,没有任何默认的反序列化器可以通过任何预设来完成。

另外,你不需要使用 switch/cases,你可以使用反射来填充你的道具。

于 2016-08-08T06:37:35.430 回答
0

据我所知,这个结构没有默认的反序列化器。正如@Nigrimmist 所说-您不需要使用switch-case。

您可以XmlReader用于读取 xml,或使用XmlDocument. 下面的解析示例代码。我没有测试过,所以小心使用。

public T ConvertFromXml<T>(string yourXml, params object[] activationData)
    where T : new() //if your type has default constructor
{
    var resultType = typeof(T);
    //if your type has not default constructor
    var result = (T)Activator.CreateInstance(typeof(T), activationData);
    //if it has default constructor
    var result = new T();

    //create an instance of xml reader
    var xmlDocument = new XmlDocument();
    xmlDocument.LoadXml(yourXml);

    //expecting that your xml response will always have the same structure
    if (xmlDocument.SelectSingleNode("RESPONSE/SINGLE") != null)
    {
        foreach(XmlNode node in xmlDocument.SelectNodes("RESPONSE/SINGLE/KEY"))
        {
            var prop = resultType.GetProperty(node.Attributes["name"].Value);
            if (prop != null)
            {
                var value = prop.SelectSingleNode("Value").Value;
                //if value does not exist - check if null value can be assigned
                if (value == null && (!prop.PropertyType.IsValueType || (Nullable.GetUnderlyingType(prop.PropertyType) != null)))
                {
                    prop.SetValue(result, value); //explicitly setting the required value
                } 
                else if (value != null)
                {
                    //we receiving the string, so for number parameters we should parse it
                    if (IsNumberType(prop.PropertyType))
                    {
                        prop.SetValue(result, double.Parse(value));
                    }
                    else
                    {
                        prop.SetValue(result, value); //will throw an exception if property type is not a string
                    }

                    //need some additional work for DateTime, TimeSpan, arrays and other
                }

            }
            else 
            {
                //remove next line if you do not need a validation for property presence
                throw new ArgumentException("Could not find the required property " + node.Attributes["name"].Value);
            }
        }
    }
    else 
    {
        throw new ArgumentException("Xml has invalid structure");
    }

    return result;
}

private bool IsNumberType(Type t)
{
    var numberTypes = new[] {
        typeof(byte), typeof(short), typeof(int), typeof(long),
        typeof(float), typeof(double), typeof(decimal),
        typeof(byte?), typeof(short?), typeof(int?), typeof(long?),
        typeof(float?), typeof(double?), typeof(decimal?)
    };

    return numberTypes.Contains(t);
}

和示例用法:

var xml = /*your method to get string representing a xml*/;
return ConvertFromXml<Course>(xml);

如果您可以以 json 表示而不是 xml 获得响应,请查看Json.Net使用简单的库。

于 2016-08-08T07:07:31.023 回答
0

使用 xml linq。请参阅下面的代码。我只解析了 id 但您可以添加其他属性。

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

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            Course course = new Course();
            course.ReadXML(FILENAME);
        }
    }
    public class Course
    {
        public static List<Course> courses = new List<Course>(); 

        public int id { get; set; }
        public string shortname { get; set; }  //short name of course
        public string fullname { get; set; }   //long name of course
        public int enrolledusercount { get; set; }  //Number of enrolled users in this course
        public string idnumber { get; set; }   //id number of course
        public int visible { get; set; }  //1 means visible, 0 means hidden course
        public string summary { get; set; }
        public int summaryformat { get; set; } //summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN)
        public string format { get; set; } //course format: weeks, topics, social, site
        public int showgrades { get; set; } //true if grades are shown, otherwise false
        public string lang { get; set; } //forced course language
        public int enablecompletion { get; set; } //true if completion is enabled, otherwise false

        public void ReadXML(string filename)
        {
            XDocument doc = XDocument.Load(filename);
            courses = doc.Descendants("SINGLE").Select(x => ReadKeys(x)).ToList();
        }
        public Course ReadKeys(XElement single)
        {
            Course newCourse = new Course();
            foreach(XElement key in single.Descendants("KEY"))
            {
                switch(key.Attribute("name").Value)
                {
                    case "id" :
                        newCourse.id = (int)key.Element("VALUE");
                        break;
                }
            }
            return newCourse;
        }
    }
}
于 2016-08-08T07:17:53.947 回答