【问题标题】:Alternative for deserializing XML with XmlSerializer使用 XmlSerializer 反序列化 XML 的替代方法
【发布时间】:2017-12-27 13:52:33
【问题描述】:

我正在编写一个 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 应用程序中工作?

【问题讨论】:

    标签: c# uwp xmlserializer


    【解决方案1】:

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

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

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

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

    2. Json.NET。此序列化程序支持将 XML 转换为 JSON,如 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>();
    

    注意事项:

    工作.Net fiddle

    【讨论】:

      【解决方案2】:

      鉴于您问题的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();
      

      这里我使用XAttributeexplicit casting operatorName 属性转换为字符串值。还有explicit casting operators 用于XElement 将元素值转换为原始值,例如stringintnullable DateTime 等。如上所示,在填充 c# 类型时可以使用这些运算符。

      (切换到DataContractSerializer 不会更容易,因为这个序列化程序does not support XML attributes out of the box 意味着无论如何您都必须手动进行操作。)

      工作示例.Net fiddle

      【讨论】:

      • 非常感谢。有没有一种方法可以创建 c# 元素,而不必将每个 XML 属性分配给代码中的对象属性,就像在这一行中所做的那样:.Select(e =&gt; new Element { Name = (string)e.Attribute("Name") } )?而是像 XmlSerializer 的 Deserialize 函数那样自动完成?
      猜你喜欢
      • 1970-01-01
      • 2023-04-05
      • 2013-04-26
      • 1970-01-01
      • 1970-01-01
      • 2020-12-09
      • 2015-09-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多