C# 如何使用泛型反序列化动态XML?附常规实现示例
First off, your existing strongly-typed deserialization works great for fixed XML structures, but a generic approach can make this logic reusable across different schemas. Depending on what you mean by "dynamic," here are two practical, actionable solutions:
1. Reusable Generic Deserialization Method (Strongly-Typed)
If you still want to define model classes for your XML structures but avoid repeating deserialization boilerplate, a generic helper method is perfect. It lets you deserialize any XML into its corresponding strongly-typed class with a single call.
Implementation:
using System.Xml.Serialization; using System.IO; public static class XmlSerializationHelper { // Generic method to deserialize any XML string into a specified type public static T Deserialize<T>(string xml) where T : class { if (string.IsNullOrEmpty(xml)) throw new ArgumentNullException(nameof(xml)); var serializer = new XmlSerializer(typeof(T)); using var stringReader = new StringReader(xml); return (T)serializer.Deserialize(stringReader); } // Optional: Generic serialization method for round-tripping data public static string Serialize<T>(T obj) where T : class { if (obj == null) throw new ArgumentNullException(nameof(obj)); var serializer = new XmlSerializer(typeof(T)); using var stringWriter = new StringWriter(); serializer.Serialize(stringWriter, obj); return stringWriter.ToString(); } }
Usage with Your Existing Model:
You can use this helper with your existing Response and Results classes seamlessly:
string xml = @"<RESPONSE version=""10""> <RESULTS MyAttribute=""1"" /> </RESPONSE>"; // Deserialize into your strongly-typed Response class var response = XmlSerializationHelper.Deserialize<Response>(xml); // Access the attribute value bool attributeValue = response.Results.MyAttribute;
This approach keeps type safety while making deserialization logic reusable across all your XML models.
2. Dynamic Generic Structure for Arbitrary XML
If you need to handle frequently changing XML structures (without pre-defining model classes), create a generic, flexible container class that captures attributes, child elements, and text content dynamically.
Implementation:
using System.Collections.Generic; using System.Linq; using System.Xml.Serialization; [XmlRoot] public class DynamicXmlNode<T> where T : class, new() { // Capture all attributes in a list (for easy access) [XmlAnyAttribute] public List<System.Xml.XmlAttribute> Attributes { get; set; } = new(); // Capture any child elements (recursive generic structure) [XmlAnyElement] public List<DynamicXmlNode<object>> ChildNodes { get; set; } = new(); // Capture text content if present [XmlText] public string TextContent { get; set; } // Optional: Strongly-typed content to mix dynamic and static logic public T TypedContent { get; set; } = new(); // Helper method to get an attribute value by name public string GetAttributeValue(string attributeName) { return Attributes.FirstOrDefault(a => a.Name == attributeName)?.Value; } }
Usage:
For your sample XML, you can deserialize into this dynamic structure and access elements/attributes without pre-defined classes:
string xml = @"<RESPONSE version=""10""> <RESULTS MyAttribute=""1"" /> </RESPONSE>"; var dynamicNode = XmlSerializationHelper.Deserialize<DynamicXmlNode<object>>(xml); // Get the root element's version attribute string version = dynamicNode.GetAttributeValue("version"); // Find the RESULTS child node and get its MyAttribute value var resultsNode = dynamicNode.ChildNodes.First(n => n.LocalName == "RESULTS"); bool myAttributeBool = bool.Parse(resultsNode.GetAttributeValue("MyAttribute"));
This approach gives you full flexibility to handle any XML structure dynamically while using generics for type consistency in your container.
Bonus: Fully Dynamic ExpandoObject Approach
If you want ultimate flexibility without generics, combine the helper method with ExpandoObject to get a fully dynamic object where you can access properties directly:
using System.Dynamic; using System.Xml; public static dynamic DeserializeToDynamic(string xml) { var doc = new XmlDocument(); doc.LoadXml(xml); return ConvertXmlNodeToExpando(doc.DocumentElement); } private static dynamic ConvertXmlNodeToExpando(XmlNode node) { var expando = new ExpandoObject() as IDictionary<string, object>; // Add all attributes to the expando object foreach (XmlAttribute attr in node.Attributes) { expando[attr.Name] = attr.Value; } // Add child nodes foreach (XmlNode childNode in node.ChildNodes) { if (childNode is XmlElement element) { if (!expando.ContainsKey(element.Name)) expando[element.Name] = new List<dynamic>(); ((List<dynamic>)expando[element.Name]).Add(ConvertXmlNodeToExpando(element)); } else if (childNode is XmlText textNode && !string.IsNullOrWhiteSpace(textNode.Value)) { expando["TextContent"] = textNode.Value; } } return expando; }
Usage:
dynamic dynamicResponse = DeserializeToDynamic(xml); string version = dynamicResponse.version; bool myAttribute = bool.Parse(dynamicResponse.RESULTS[0].MyAttribute);
This lets you interact with the XML as if it were a native C# object, no model classes required.
内容的提问来源于stack exchange,提问作者JOSEFtw

