如何将XML元素值反序列化到C#自定义类的对应属性中
解决方案
有两种不需要手动逐个提取属性的实现方案,如下:
方案1:Linq to XML + 反射通用映射(无需修改现有Invoice类,推荐)
这个方案不需要给Invoice加任何特性或者接口适配,直接通过反射自动匹配属性名和XML里的name节点值,自动做类型转换:
首先要注意你提供的XML没有根节点,解析前需要先包裹一个根标签避免报错,完整代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Xml.Linq; // 你的原有Invoice类不需要任何修改 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; } } public static class XmlToInvoiceConverter { public static List<Invoice> DeserializeInvoices(string xmlContent) { // 给原XML加根节点,否则无法解析 var wrappedXml = $"<root>{xmlContent}</root>"; var root = XElement.Parse(wrappedXml); var invoices = new List<Invoice>(); // 取所有document节点 foreach (var docNode in root.Elements("document")) { // 把当前document的所有属性转成字典,key是name节点值,value是value节点值 var propDict = docNode.Element("propertyValues") .Elements("propertyValue") .ToDictionary( p => p.Element("name").Value.Trim(), p => p.Element("value").Value.Trim() ); var invoice = new Invoice(); var invoiceProps = typeof(Invoice).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in invoiceProps) { // 忽略不可写的属性 if (!prop.CanWrite) continue; // 匹配XML里的key,需严格大小写匹配可删除StringComparison.OrdinalIgnoreCase参数 if (propDict.TryGetValue(prop.Name, StringComparison.OrdinalIgnoreCase, out var valueStr)) { // 做类型转换 object convertedValue = null; if (prop.PropertyType == typeof(DateTime)) { // 适配你的日期格式dd-MM-yyyy convertedValue = DateTime.ParseExact(valueStr, "dd-MM-yyyy", null); } else { convertedValue = Convert.ChangeType(valueStr, prop.PropertyType); } prop.SetValue(invoice, convertedValue); } } invoices.Add(invoice); } return invoices; } }
调用方法非常简单,直接把你原有的XML字符串传进去就能得到List<Invoice>结果。
方案2:实现IXmlSerializable接口,用原生XmlSerializer反序列化
如果你希望用系统自带的XmlSerializer标准流程,可以给Invoice类实现IXmlSerializable接口,代码如下:
using System; using System.Xml; using System.Xml.Serialization; [XmlRoot("document")] public class Invoice : IXmlSerializable { 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; } public System.Xml.Schema.XmlSchema GetSchema() => null; public void ReadXml(XmlReader reader) { reader.ReadToFollowing("propertyValues"); var propReader = reader.ReadSubtree(); while (propReader.ReadToFollowing("propertyValue")) { var name = propReader.ReadElementContentAsString("name", ""); var value = propReader.ReadElementContentAsString("value", ""); switch (name) { case nameof(InvoiceId): InvoiceId = value; break; case nameof(CompanyName): CompanyName = value; break; case nameof(VendorName): VendorName = value; break; case nameof(GrossAmt): GrossAmt = decimal.Parse(value); break; case nameof(InvoiceDate): InvoiceDate = DateTime.ParseExact(value, "dd-MM-yyyy", null); break; } } reader.Read(); } public void WriteXml(XmlWriter writer) { // 如果不需要序列化回XML可以留空 throw new NotImplementedException(); } }
反序列化调用代码:
var serializer = new XmlSerializer(typeof(List<Invoice>), new XmlRootAttribute("root")); var wrappedXml = $"<root>{yourOriginalXmlString}</root>"; using (var strReader = new StringReader(wrappedXml)) { var invoices = (List<Invoice>)serializer.Deserialize(strReader); }
两种方案都可以满足需求,第一种通用型更强,后续如果Invoice加属性不需要修改转换逻辑,自动适配;第二种符合.NET XML序列化的标准规范。
内容的提问来源于stack exchange,提问作者tiki90
相关产品推荐
相关产品推荐

