使用 XmlSerializer 反序列化 XML 的替代方法

Alternative for deserializing XML with XmlSerializer

我正在编写 UWP 应用程序并希望将 xml 中的列表反序列化为对象列表:

<List>
    <Element Name="A">
        <SubElement Name="A1"> 
            <Color> Blue </Color> 
            <Color> Red </Color> 
        </SubElement>
        <SubElement Name="A2"> 
            <Color> Blue </Color> 
            <Color> Green </Color>  
        </SubElement>
    </Element>
    <Element Name="B">
        <SubElement Name="B1"> 
            <Color> Yellow </Color> 
            <Color> Red </Color> 
        </SubElement>
        <SubElement Name="B2"> 
            <Color> Yellow </Color> 
            <Color> Green </Color>  
        </SubElement>
    </Element>
    <Element Name="C"/>
        <SubElement Name="C1"> 
            <Color> Purple </Color> 
            <Color> Red </Color> 
        </SubElement>
        <SubElement Name="C2"> 
            <Color> Purple </Color> 
            <Color> Green </Color>  
        </SubElement>
    </Element>
</List> 

类 看起来像这样:

public class Element
{
    [XmlAttribute]
    public string Name { get; set; }

    [XmlElement("SubElement")]
    public List<SubElement> _subelement { get; set; }
}

public class SubElement
{
      [XmlElement("Color")]
      public string Color{ get; set; }

      [XmlAttribute("Name")]
      public string Name { get; set; } 
}

我的 C# 代码如下所示:

XDocument doc = XDocument.Load("list.xml");
XElement node = doc.Descendants(XName.Get("List")).FirstOrDefault();
var serializer = new XmlSerializer(typeof(List<Element>), new XmlRootAttribute("List"));
var elementList = serializer.Deserialize(node.CreateReader()) as List<Element>;

在调试模式下,此应用程序运行良好。在发布模式下,我得到一个 PlatformNotSupportedException 错误,如 this 线程。我发现问题一定与项目设置中的 Compile with .NET Native tool chain 选项有关。但我没有找到解决问题的方法。

我的问题:上面显示的代码是否有一个简单的替代方法来将 XML 反序列化为不使用 XmlSerializer 并在 UWP 应用程序中工作的对象?

鉴于您的问题 initial version 中显示的 XML:

<List>
    <Element Name="A"/>
    <Element Name="B"/>
    <Element Name="C"/>
</List> 

假设您的 Element 类型如下:

public class Element
{
    [XmlAttribute]
    public string Name { get; set; }
}

那么这个类型很简单,你可以像这样手动构造它:

var list = doc
    // Get the first elements named List
    .Descendants("List").Take(1)
    // Get all children of List named Element
    .SelectMany(l => l.Elements("Element"))
    // Get the attribute of Element named Name, cast its value to a string, 
    // and create a c# Element from it using the specified name.
    .Select(e => new Element { Name = (string)e.Attribute("Name") } )
    // Materialize as a List<Element>
    .ToList();

这里我使用了 explicit casting operator of XAttribute to convert the Name attribute to a string value. There are also explicit casting operators for XElement to convert an element value to a primitive such as a string, an int or a nullable DateTime 等等。如上所示填充 c# 类型时可以使用这些运算符。

(切换到 DataContractSerializer would not be easier since this serializer does not support XML attributes out of the box 意味着您无论如何都必须手动操作。)

工作示例 .Net fiddle

在您的 rewritten question 中,您的 XML 似乎具有混合元素和属性的任意复杂性。您正在寻找一种不使用 XmlSerializer 自动反序列化此 XML 的方法,这基本上相当于 编写另一个 XML 序列化程序

因为这很重要(并且超出了 Whosebug 答案的范围),一个快速的解决方法是将 XML 转换为其他一些序列化程序可识别的格式,并使用它。可能性包括:

  1. DataContractSerializer。此序列化程序用于 XML 反序列化,但不支持:

    • XML attribute deserialization.
    • 将序列(例如 <Element /><Element/>)反序列化为没有外部元素的集合。

    因此,虽然可以使用此序列化程序将 XML 反序列化为某些适当的数据模型,但需要使用 LINQ to XML 进行大量预处理。

  2. Json.NET. This serializer supports conversion of XML to JSON as documented in Converting between JSON and XML 因此可能适合您的需要。

如果您选择选项 #2,您可以按如下方式定义您的数据模型:

public class RootObject
{
    [JsonConverter(typeof(SingleOrArrayConverter<Element>))]
    public List<Element> Element { get; set; }
}

public class Element
{
    [XmlAttribute]
    [JsonProperty("@Name")]
    public string Name { get; set; }

    [XmlElement("SubElement")]
    [JsonProperty("SubElement")]
    [JsonConverter(typeof(SingleOrArrayConverter<SubElement>))]
    public List<SubElement> _subelement { get; set; }
}

public class SubElement
{
    [XmlElement("Color")]
    [JsonConverter(typeof(SingleOrArrayConverter<string>))]
    public List<string> Color { get; set; }

    [XmlAttribute("Name")]
    [JsonProperty("@Name")]
    public string Name { get; set; }
}

并反序列化如下:

var doc = XDocument.Parse(xmlString);

// Convert the XDocument to an intermediate JToken hierarchy.
var converter = new Newtonsoft.Json.Converters.XmlNodeConverter { OmitRootObject = true };
var rootToken = JObject.FromObject(doc, JsonSerializer.CreateDefault(new JsonSerializerSettings { Converters = { converter } }));

// Convert the JTOken to a RootObject
var rootObj = rootToken.ToObject<RootObject>();

备注:

  • 在你的问题中你的 SubElement 有一个 Color 属性,但在你的 XML 中显然有多个 <Color>每个 <SubElement> 的元素。因此我不得不将其更改为 public List<string> Color { get; set; }.

  • SingleOrArrayConverter<> 逐字取自 this answer to How to handle both a single item and an array for the same property using JSON.net by Brian Rogers。需要处理单个子节点转换为 JSON 对象而不是 JSON 数组的情况。

正在工作.Net fiddle