如何在.NET Core中将特定结构XML反序列化为Person POCO模型?
在.NET Core中将name-value结构的XML反序列化为自定义POCO模型
问题场景
你调用Web服务得到了这种由<return>节点包裹name和value键值对的XML,自动生成的类和你想要的Person POCO结构不匹配,常规XML反序列化方法没法直接映射,下面给你两种实用的解决方案:
先回顾下你的输入信息:
原始XML
<returns> <return> <name>firstName</name> <value>John</value> </return> <return> <name>lastName</name> <value>Doe</value> </return> <return> <name>dateOfBirth</name> <value>01-01-1900 00:00</value> </return> <return> <name>address</name> <value>100, Example Street</value> </return> </returns>
Visual Studio自动生成的类
// NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0. /// <remarks/> [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)] public partial class returns { private returnsReturn[] returnField; /// <remarks/> [System.Xml.Serialization.XmlElementAttribute("return")] public returnsReturn[] @return { get { return this.returnField; } set { this.returnField = value; } } } /// <remarks/> [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] public partial class returnsReturn { private string nameField; private string valueField; /// <remarks/> public string name { get { return this.nameField; } set { this.nameField = value; } } /// <remarks/> public string value { get { return this.valueField; } set { this.valueField = value; } } }
目标POCO
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public DateTime DateOfBirth { get; set; } public string Address { get; set; } }
方案一:中间类转换 + 手动映射(推荐新手使用)
这是最直观、成本最低的方法:先用自动生成的类完成XML到中间对象的反序列化,再把中间对象的键值对映射到Person类。优点是代码简单,容易理解和维护,适合字段数量不多的场景。
代码示例
using System.Xml.Serialization; using System.IO; using System.Linq; // 假设xmlContent是你从Web服务获取的XML字符串 string xmlContent = "..."; // 1. 反序列化到自动生成的returns类 var serializer = new XmlSerializer(typeof(returns)); Person person; using (var reader = new StringReader(xmlContent)) { var returnsObj = (returns)serializer.Deserialize(reader); // 2. 把returns里的键值对转成字典,方便映射 var valueMap = returnsObj.@return.ToDictionary(item => item.name, item => item.value); // 3. 映射到Person对象 person = new Person { FirstName = valueMap["firstName"], LastName = valueMap["lastName"], // 注意日期格式要和XML里的匹配,用ParseExact避免格式歧义 DateOfBirth = DateTime.ParseExact(valueMap["dateOfBirth"], "dd-MM-yyyy HH:mm", null), Address = valueMap["address"] }; }
优化点:处理缺失字段
如果XML可能缺少某些字段,直接用valueMap["key"]会抛出KeyNotFoundException,建议改用TryGetValue做安全处理:
if (valueMap.TryGetValue("firstName", out var firstName)) { person.FirstName = firstName; } // 其他字段同理
方案二:自定义反序列化逻辑(直接映射到Person)
如果你想跳过中间类,直接把XML反序列化成Person,可以让Person类实现IXmlSerializable接口,手动控制XML的读取和写入逻辑。这种方法更灵活,适合需要频繁处理这种结构XML的场景。
实现IXmlSerializable的Person类
using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using System.Globalization; public class Person : IXmlSerializable { public string FirstName { get; set; } public string LastName { get; set; } public DateTime DateOfBirth { get; set; } public string Address { get; set; } // 这个方法返回null即可,无需额外实现 public XmlSchema GetSchema() => null; // 核心:自定义XML读取逻辑 public void ReadXml(XmlReader reader) { // 跳过根节点<returns>的开始标签 reader.ReadStartElement("returns"); var valueMap = new Dictionary<string, string>(); // 遍历所有<return>节点 while (reader.NodeType != XmlNodeType.EndElement || reader.Name != "returns") { if (reader.Name == "return" && reader.NodeType == XmlNodeType.Element) { reader.ReadStartElement("return"); string fieldName = null; string fieldValue = null; // 读取当前<return>里的name和value while (reader.NodeType != XmlNodeType.EndElement || reader.Name != "return") { if (reader.Name == "name") { fieldName = reader.ReadElementContentAsString(); } else if (reader.Name == "value") { fieldValue = reader.ReadElementContentAsString(); } else { // 跳过未知节点,避免解析失败 reader.Skip(); } } if (!string.IsNullOrEmpty(fieldName)) { valueMap[fieldName] = fieldValue; } // 跳过当前<return>的结束标签 reader.ReadEndElement(); } else { reader.Skip(); } } // 把字典里的值映射到Person的属性 if (valueMap.TryGetValue("firstName", out var firstName)) FirstName = firstName; if (valueMap.TryGetValue("lastName", out var lastName)) LastName = lastName; if (valueMap.TryGetValue("dateOfBirth", out var dobStr) && DateTime.TryParseExact(dobStr, "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dob)) DateOfBirth = dob; if (valueMap.TryGetValue("address", out var address)) Address = address; // 跳过根节点<returns>的结束标签 reader.ReadEndElement(); } // 如果需要把Person序列化成这种XML结构,可以实现这个方法 public void WriteXml(XmlWriter writer) { writer.WriteStartElement("returns"); WriteReturnNode(writer, "firstName", FirstName); WriteReturnNode(writer, "lastName", LastName); WriteReturnNode(writer, "dateOfBirth", DateOfBirth.ToString("dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture)); WriteReturnNode(writer, "address", Address); writer.WriteEndElement(); } // 辅助方法:生成单个<return>节点 private void WriteReturnNode(XmlWriter writer, string name, string value) { if (string.IsNullOrEmpty(value)) return; writer.WriteStartElement("return"); writer.WriteElementString("name", name); writer.WriteElementString("value", value); writer.WriteEndElement(); } }
使用自定义类反序列化
using System.Xml.Serialization; using System.IO; string xmlContent = "..."; var serializer = new XmlSerializer(typeof(Person)); Person person; using (var reader = new StringReader(xmlContent)) { person = (Person)serializer.Deserialize(reader); }
内容的提问来源于stack exchange,提问作者adamasan
相关产品推荐
相关产品推荐

