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

5

我正在编写一个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错误,就像这个线程中所述的那样。我发现问题一定与项目设置中的使用 .NET Native 工具链编译选项有关。但是我没有找到解决问题的方法。
我的问题是:是否有一个简单的替代上面显示的代码来将XML反序列化为对象,该代码不使用XmlSerializer,可以在UWP应用程序中工作?
2个回答

8
在您的重写问题中,您的XML似乎具有任意复杂性,其中包含元素和属性的混合。您正在寻找一种自动反序列化此XML的方法,而不使用XmlSerializer,这基本上相当于编写另一个XML序列化程序
由于这是非常复杂的(超出了stackoverflow答案的范围),一个快速的解决方法是将XML转换为其他序列化程序识别的格式,并使用它。可能的选择包括:
  1. DataContractSerializer。该序列化程序适用于XML反序列化,但不支持:

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

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

  2. Json.NET。该序列化程序支持将XML转换为JSON,如在JSON和XML之间转换中所述,因此可能适合您的需求。

如果您选择第二种选项,则可以定义以下数据模型:

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中,每个<SubElement>显然都有多个<Color>元素。因此,我将其更改为public List<string> Color { get; set; }

  • SingleOrArrayConverter<>直接从答案中获取,该答案来源于如何使用JSON.net处理同一属性的单个项目和数组Brian Rogers 编写。它用于处理将单个子节点转换为JSON对象而不是JSON数组的情况。

可工作的.Net fiddle


1
在你的问题的初始版本中提供的XML如下:
<List>
    <Element Name="A"/>
    <Element Name="B"/>
    <Element Name="C"/>
</List> 

假设您的“Element”类型如下所示:

并且假设您的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();

在这里,我正在使用XAttribute显式转换运算符Name属性转换为字符串值。还有显式转换运算符用于XElement,可以将元素值转换为诸如string, int可空的DateTime等原始数据类型。当填充C#类型时,可以使用这些操作符,如上面所示。

(转换到 DataContractSerializer 并不容易,因为该序列化程序 默认不支持 XML 属性,这意味着您仍然需要手动处理一些内容。)

.Net fiddle 的示例工作。


非常感谢。有没有一种方法可以在代码中不必像这行代码那样将每个XML属性分配给对象属性来创建c#元素,而是像XmlSerializer的Deserialize函数自动执行? - SimpleNotGood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接