简体   繁体   中英

How to deserialise xml element values into C# class properties

This is my first time posting on StackOverflow so pardon any mistakes. Given this 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>

Is there an easier way to deserialise it into a C# class eg.

    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; }
    }

Deserialisation is straightforward when the xml element name maps to the property name but in this case it is a value. Trying to avoid traversing the xml and manually extracting the values out.

At least with the standard .Net XMLSerializer there's no way to decorate your class with attributes to get the serialization working as you would like. But you could work around that using a class structure like this:

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; }
}

The Invoice class would still look the same (at least for reading, writing needs a little bit more work, but I hope you get the idea). For sure there needs to be some more meaningful error handling.

The xml string would need to be wrapped in an <InvoiceList>...</InvoiceList> tag for this to work:

<?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>

The deserialization itself:

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}");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM