CodeGym /Courses /C# SELF /Reading and writing XML: X...

Reading and writing XML: XmlSerializer

C# SELF
Level 48 , Lesson 0
Available

1. Introduction

XML (eXtensible Markup Language) is an extensible markup language, a popular format for storing and exchanging complex structured data. Unlike JSON, XML isn't concise, but it's still widely used in banking, exchanges between government services, documents and integrations with 1C, SAP, Oracle, etc. Some data in Windows and .NET (for example, configs) are still stored in XML.

Visually XML looks like HTML, but without a fixed set of tags like <body> and <div> — you define the structure of your elements. Example:

<Person>
  <FirstName>Ivan</FirstName>
  <LastName>Petrov</LastName>
  <Age>30</Age>
</Person>

XML basics and mapping to C# objects

Unlike JSON, XML has no special types for arrays or booleans — everything is represented as elements and attributes. List structures are implemented as repeated elements with the same name.

To work comfortably with XML in C#, you use the serialization/deserialization mechanism via XmlSerializer.

Serialization is converting a C# object to an XML string. Deserialization is the reverse: getting a C# object from XML.

This works "out of the box" if your classes meet a set of requirements — we'll cover them below.

Why use XmlSerializer?

XmlSerializer is the standard .NET tool (namespace System.Xml.Serialization) for converting objects to XML and back. It's simple, type-safe and suitable for import/export, configs, exchanging data between systems and integrations with government services.

2. Example: serializing an object

Say we have a user model:

public class User
{
    public string FirstName { get; set; }
    public string LastName  { get; set; }
    public int Age          { get; set; }
}

Serialize an object to XML:

using System.Xml.Serialization;

User user = new User { FirstName = "Ivan", LastName = "Petrov", Age = 30 };
// XmlSerializer requires a type
XmlSerializer serializer = new XmlSerializer(typeof(User));

// Write XML to a file
using FileStream fs = new FileStream("user.xml", FileMode.Create);
serializer.Serialize(fs, user);

What happens?

- We create an instance of the User class.
- We create an XmlSerializer instance for the User type.
- We open a file with FileStream.
- We call Serialize(), which converts the object to XML and writes it to the file.

Contents of the user.xml file:

<?xml version="1.0"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <FirstName>Ivan</FirstName>
  <LastName>Petrov</LastName>
  <Age>30</Age>
</User>

Simple — minimal code and you get XML.

3. Deserialization: reading XML back into an object

Read the created XML and restore the C# object:

// Open the XML file
using FileStream fs = new FileStream("user.xml", FileMode.Open);
User? loadedUser = serializer.Deserialize(fs) as User;
Console.WriteLine($"{loadedUser?.FirstName} {loadedUser?.LastName}, age: {loadedUser?.Age}");

The Deserialize() method returns object, so we cast to User (using as). If the file is valid and matches the model — the object will be fully restored.

4. How serialization works: common rules

XML serialization requires following rules:

• The class to serialize must have a public parameterless constructor (public without parameters).

• Serializable properties must be public: both getter and setter are needed. Private members are ignored.

• Read-only properties, as well as static and abstract properties, don't work.

If rules are violated you'll get an InvalidOperationException with a message like "XmlSerializer cannot serialize this type".

5. Serializing collections: arrays and lists

If you need to serialize multiple users — wrap them in a container class:

public class UserList
{
    public List<User> Users { get; set; } = new List<User>();
}

Create the collection and serialize:

UserList users = new UserList
{
    Users = new List<User>
    {
        new User { FirstName = "Ivan", LastName = "Petrov", Age = 30 },
        new User { FirstName = "Maria", LastName = "Ivanova", Age = 25 }
    }
};

XmlSerializer serializer = new XmlSerializer(typeof(UserList));
using FileStream fs = new FileStream("users.xml", FileMode.Create);
serializer.Serialize(fs, users);

Resulting XML:

<?xml version="1.0"?>
<UserList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Users>
    <User>
      <FirstName>Ivan</FirstName>
      <LastName>Petrov</LastName>
      <Age>30</Age>
    </User>
    <User>
      <FirstName>Maria</FirstName>
      <LastName>Ivanova</LastName>
      <Age>25</Age>
    </User>
  </Users>
</UserList>

Can you serialize just List<User>? Yes, but the root element will be named ArrayOfUser, which may not be acceptable for integrations.

6. Attributes to control serialization

You can control XML format using attributes: rename elements, serialize as attributes, ignore fields, etc. Useful attributes: [XmlElement], [XmlAttribute], [XmlArray], [XmlIgnore].

Serializing as attributes

Write first and last name as attributes, and age as an element:

public class User
{
    [XmlAttribute]
    public string FirstName { get; set; }

    [XmlAttribute]
    public string LastName { get; set; }

    [XmlElement]
    public int Age { get; set; }
}

Result:

<User FirstName="Ivan" LastName="Petrov">
  <Age>30</Age>
</User>

Controlling element names

Rename tags to human-friendly names:

public class User
{
    [XmlElement("Imya")]
    public string FirstName { get; set; }

    [XmlElement("Familiya")]
    public string LastName { get; set; }

    [XmlElement("Vozrast")]
    public int Age { get; set; }
}

Resulting XML:

<User>
  <Imya>Ivan</Imya>
  <Familiya>Petrov</Familiya>
  <Vozrast>30</Vozrast>
</User>

Ignoring properties

If a property shouldn't be serialized — mark it with [XmlIgnore]:

public class User
{
    public string FirstName { get; set; }

    [XmlIgnore]
    public string InternalCode { get; set; }
}

7. Serializing hierarchies and nested objects

Nested classes easily become nested XML elements:

public class Address
{
    public string City { get; set; }
    public string Street { get; set; }
}

public class User
{
    public string FirstName { get; set; }
    public Address Address { get; set; }
}
var user = new User
{
    FirstName = "Anna",
    Address = new Address { City = "Neongrad", Street = "Vyazovaya" }
};

XmlSerializer serializer = new XmlSerializer(typeof(User));
using var fs = new FileStream("user_with_address.xml", FileMode.Create);
serializer.Serialize(fs, user);

XML:

<User>
  <FirstName>Anna</FirstName>
  <Address>
    <City>Neongrad</City>
    <Street>Vyazovaya</Street>
  </Address>
</User>

8. Serialize to/from string (no files)

Serialize to a string

using System.Text;

var serializer = new XmlSerializer(typeof(User));
using var stringWriter = new StringWriter();

serializer.Serialize(stringWriter, user);
string xml = stringWriter.ToString();
Console.WriteLine(xml);

Deserialize from a string

var xml = "<User><FirstName>Anna</FirstName></User>";
using var stringReader = new StringReader(xml);
User user = (User)serializer.Deserialize(stringReader);
Console.WriteLine(user.FirstName);

9. Example: app data export and import

Export/import a list of users to an XML file:

public static void ExportUsers(UserList users, string path)
{
    var serializer = new XmlSerializer(typeof(UserList));
    using var fs = new FileStream(path, FileMode.Create);
    serializer.Serialize(fs, users);
}

public static UserList ImportUsers(string path)
{
    var serializer = new XmlSerializer(typeof(UserList));
    using var fs = new FileStream(path, FileMode.Open);
    return (UserList)serializer.Deserialize(fs);
}

Use cases: backups, exchanging data with other services, integrations. On interviews you’ll see questions about XmlSerializer more often than you might expect — especially at companies working with government services and banks.

10. Common mistakes and behaviors

If a field is not public, it won't be serialized.

Read-only properties are ignored.

Properties typed as interfaces or abstract classes are not supported by the serializer.

The DateTime type is serialized in XSD format (see the documentation).

Collections like arrays and List<T> behave predictably, while ObservableCollection<T> can cause surprises for third-party systems.

Encoding quirks: when writing via StreamWriter without an explicit encoding the result may end up in UTF-16; for compatibility systems often expect UTF-8 — specify encoding explicitly.

2
Task
C# SELF, level 48, lesson 0
Locked
Serializing a List of Objects
Serializing a List of Objects
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION