How XML maps to the classes
| XML | C# |
|---|---|
| The root element | A class with [XmlRoot("name", Namespace = "...")] |
<Total>44.80</Total> | [XmlElement("Total")] public decimal Total |
number="INV-1" | [XmlAttribute("number")] public string Number |
An element that repeats: <item><item> | [XmlElement("item")] public List<Item> Item |
A list in a wrapper: <Lines><Line/><Line/></Lines> | [XmlArray("Lines")] [XmlArrayItem("Line")] public List<Line> Lines |
Text next to attributes: <Price currency="EUR">5</Price> | A class with [XmlAttribute]s and [XmlText] public decimal Value |
A prefixed element: <cac:Customer> | [XmlElement("Customer", Namespace = "urn:...")] |
Every element with the same name becomes one class, wherever it appears, as in an XML schema. Attributes and elements missing from some occurrences become optional.
XmlSerializer details the converter handles
- Optional attributes of value types are a problem, since XmlSerializer cannot write
int?as an attribute. The converter writesint Quantityplus[XmlIgnore] bool QuantitySpecified: it is true when the attribute was there, and set it to true to write the attribute. - Optional elements become nullable (
int?,Customer?). A nullint?is written back as<Count xsi:nil="true" />, not left out. - Dates with an offset become
DateTimeOffset. Read into aDateTime, "2026-09-27T10:15:00+02:00" is converted to the server's local time: 10:15 on a server in Berlin, 08:15 on one set to UTC. - Leading zeros (
01234, zip codes, IDs) stay strings, since as numbers they would come back as1234. Elements that are empty somewhere stay strings too, because XmlSerializer cannot read an empty element as a number. - Names and namespaces must match exactly. XML is case-sensitive, and an element in another namespace is a different element. XmlSerializer skips anything that does not match, without an error.
Read and write the XML
The code under the converter reads the XML into the root class. To write it back, call serializer.Serialize(writer, value). Create each XmlSerializer once and reuse it: the constructor generates code the first time for each type.
FAQ
How do I deserialize XML to a C# object?
Generate the classes here, then new XmlSerializer(typeof(Root)).Deserialize(reader) with a StringReader or a Stream, and cast the result.
Why is my XML property always null or empty after deserializing?
The element or attribute name, or its namespace, does not match exactly. XML is case-sensitive, and XmlSerializer skips unmatched content silently.
Why do optional attributes get a Specified property?
XmlSerializer cannot write a nullable value type as an attribute; the [XmlIgnore] Specified property says whether the attribute is present.
Can System.Text.Json read XML?
No. Use XmlSerializer with these classes, DataContractSerializer, or LINQ to XML (XDocument) for ad hoc reading.
I have an XSD, not a sample. What then?
Generate classes from the schema with xsd.exe (part of the .NET Framework SDK on Windows) or a schema class generator such as XmlSchemaClassGenerator. A sample document works here and covers what your real data contains.
Related tools
- JSON to C# class converter
- JSON to YAML converter
- Byte order mark (BOM) checker (XmlWriter writes a BOM by default)
- HTML encode and decode