如何将 xml 元素值反序列化为 C# 类属性

Posted

技术标签:

【中文标题】如何将 xml 元素值反序列化为 C# 类属性【英文标题】:How to deserialise xml element values into C# class properties 【发布时间】:2021-12-13 07:20:49 【问题描述】:

这是我第一次在 *** 上发帖,如有错误请见谅。鉴于此 xml:

<document name="doc1.pdf">
  <propertyValues>      
    <propertyValue><name>InvoiceID</name><value>123</value></propertyValue>
    <propertyValue><name>CompanyName</name><value>Company1</value></propertyValue>
    <propertyValue><name>VendorName</name><value>Vendor1</value></propertyValue>    
    <propertyValue><name>GrossAmt</name><value>176.33</value></propertyValue>
    <propertyValue><name>InvoiceDate</name><value>26-03-2019</value></propertyValue>
  </propertyValues>
</document>
<document name="doc2.pdf">
  <propertyValues>      
    <propertyValue><name>InvoiceID</name><value>223</value></propertyValue>
    <propertyValue><name>CompanyName</name><value>Company2</value></propertyValue>
    <propertyValue><name>VendorName</name><value>Vendor2</value></propertyValue>    
    <propertyValue><name>GrossAmt</name><value>5300.88</value></propertyValue>
    <propertyValue><name>InvoiceDate</name><value>31-03-2019</value></propertyValue>
  </propertyValues>
</document>

是否有更简单的方法将其反序列化为 C# 类,例如。

    public class Invoice
    
        public string InvoiceId  get; set; 
        public string CompanyName  get; set; 
        public string VendorName  get; set; 
        public decimal GrossAmt  get; set; 
        public DateTime InvoiceDate  get; set; 
    

当 xml 元素名称映射到属性名称时,反序列化很简单,但在这种情况下它是一个值。尽量避免遍历xml并手动提取值。

【问题讨论】:

这能回答你的问题吗? Deserialize XML with Attributes in C# 不,因为他们的 xml 是这样的,很容易反序列化:``` 123CompName ``` 【参考方案1】:

至少对于标准的 .Net XMLSerializer,没有办法用属性来装饰您的类以使序列化按您的意愿工作。但是你可以使用这样的类结构来解决这个问题:

public class InvoiceList 
    [XmlElement("document")]
    public Invoice[] Invoices  get; set; 


public class Invoice 
    [XmlIgnore]
    public string InvoiceId => this.PropertyValues.First(p => p.Name == "InvoiceID").Value;
    [XmlIgnore]
    public string CompanyName => this.PropertyValues.First(p => p.Name == "CompanyName").Value;
    [XmlIgnore]
    public string VendorName => this.PropertyValues.First(p => p.Name == "VendorName").Value;
    [XmlIgnore]
    public decimal GrossAmt => decimal.Parse(this.PropertyValues.First(p => p.Name == "GrossAmt").Value);
    [XmlIgnore]
    public DateTime InvoiceDate => DateTime.Parse(this.PropertyValues.First(p => p.Name == "InvoiceDate").Value);
    [XmlArray("propertyValues")]
    [XmlArrayItem("propertyValue")]
    public PropertyValue[] PropertyValues  get; set; 


public class PropertyValue 
    [XmlElement("name")]
    public string Name  get; set; 
    [XmlElement("value")]
    public string Value  get; set; 

Invoice 类看起来还是一样的(至少对于阅读来说,写作需要更多的工作,但我希望你明白这一点)。当然需要一些更有意义的错误处理。

xml 字符串需要包装在 &lt;InvoiceList&gt;...&lt;/InvoiceList&gt; 标记中才能正常工作:

<?xml version="1.0" encoding="UTF-8"?>
<InvoiceList>
  <document name="doc1.pdf">
    <propertyValues>
      <propertyValue><name>InvoiceID</name><value>123</value></propertyValue>
      <propertyValue><name>CompanyName</name><value>Company1</value></propertyValue>
      <propertyValue><name>VendorName</name><value>Vendor1</value></propertyValue>
      <propertyValue><name>GrossAmt</name><value>176.33</value></propertyValue>
      <propertyValue><name>InvoiceDate</name><value>26-03-2019</value></propertyValue>
    </propertyValues>
  </document>
  <document name="doc2.pdf">
    <propertyValues>
      <propertyValue><name>InvoiceID</name><value>223</value></propertyValue>
      <propertyValue><name>CompanyName</name><value>Company2</value></propertyValue>
      <propertyValue><name>VendorName</name><value>Vendor2</value></propertyValue>
      <propertyValue><name>GrossAmt</name><value>5300.88</value></propertyValue>
      <propertyValue><name>InvoiceDate</name><value>31-03-2019</value></propertyValue>
    </propertyValues>
  </document>
</InvoiceList>

反序列化本身:

var xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><InvoiceList><document name=\"doc1.pdf\"><propertyValues><propertyValue><name>InvoiceID</name><value>123</value></propertyValue><propertyValue><name>CompanyName</name><value>Company1</value></propertyValue><propertyValue><name>VendorName</name><value>Vendor1</value></propertyValue><propertyValue><name>GrossAmt</name><value>176.33</value></propertyValue><propertyValue><name>InvoiceDate</name><value>26-03-2019</value></propertyValue></propertyValues></document><document name=\"doc2.pdf\"><propertyValues><propertyValue><name>InvoiceID</name><value>223</value></propertyValue><propertyValue><name>CompanyName</name><value>Company2</value></propertyValue><propertyValue><name>VendorName</name><value>Vendor2</value></propertyValue><propertyValue><name>GrossAmt</name><value>5300.88</value></propertyValue><propertyValue><name>InvoiceDate</name><value>31-03-2019</value></propertyValue></propertyValues></document></InvoiceList>";
using var stream = new MemoryStream();
using var writer = new StreamWriter(stream);
writer.Write(xml);
writer.Flush();
stream.Position = 0;

using var serializer = new XmlSerializer(typeof(InvoiceList));
var list = (InvoiceList)serializer.Deserialize(stream);

foreach (var i in list.Invoices) 
    Console.WriteLine($"i.InvoiceId, i.CompanyName");

【讨论】:

这个问题是没有预先验证。要么有一个明确的验证方法,要么将序列化的数据转换为一个单独的域对象,该对象一旦构建就可以保证工作。 @JonasH 这就是我所说的“肯定需要一些更有意义的错误处理”。但这主要取决于输入的质量 @TobiasSchulte 感谢您的帮助。我不知道如何定义序列化类,所以我使用了 XElement。将尝试您的解决方案。

以上是关于如何将 xml 元素值反序列化为 C# 类属性的主要内容,如果未能解决你的问题,请参考以下文章

将json键值反序列化为字典c#

使用JSON.Net将字符串属性值反序列化为类实例

XML 反序列化:在单个属性上使用 XmlAttribute 和 XmlText

如何将不同类型的子元素反序列化为基类型的列表/集合,这是相应类的属性

如何将复杂的 C# 类序列化为 XML 并将其作为 .net 核心 API 的响应发送?

如何在c#中将xml反序列化为列表