csharp-serializacion-json-xml

What is serialization in C# and how to use it with JSON and XML

  • 5 min

Serialization is the process of converting an object into a format that can be saved or transmitted.

So far, our objects live happily in RAM while the program runs. But what happens if we close the program? Poof, they disappear.

What if we want to save the state of a video game to the hard drive? Or send a client’s data to a Web API on the other side of the world? We can’t just send the RAM as is.

Serialization converts the state of an object into a transportable data format (like a JSON string, XML, or a byte sequence).

Deserialization is the reverse process: reconstructing the object in memory from that data.

Why do we serialize?

Mainly for two things:

  1. Persistence: Save objects to files or databases to “revive” them later.
  2. Transmission: Send objects over a network (HTTP/REST, Sockets) between different systems.

In .NET, we have several ways to serialize, but today the undisputed king is JSON.

JSON Serialization with System.Text.Json

For years, the third-party library Newtonsoft.Json was the de facto standard. However, since .NET Core 3.0, Microsoft introduced System.Text.Json.

It is the native, modern, secure, and extremely performance-optimized library (it works with Span<T> and low-level memory).

Serializing an object to text

Suppose we have this class:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public bool Active { get; set; }
}
Copied!

To convert a User instance to a JSON string:

using System.Text.Json;

User user = new User 
{ 
    Id = 1, 
    Name = "Luis Llamas", 
    Email = "[email protected]", 
    Active = true 
};

string jsonString = JsonSerializer.Serialize(user);

Console.WriteLine(jsonString);
// Output (minified by default):
// {"Id":1,"Name":"Luis Llamas","Email":"[email protected]","Active":true}
Copied!

Deserializing text to an object

The reverse path is just as simple. We use the generic method Deserialize<T>.

string json = @"{ ""Id"": 1, ""Name"": ""Luis"" }";

User recoveredUser = JsonSerializer.Deserialize<User>(json);

Console.WriteLine(recoveredUser.Name); // Prints: Luis
Copied!

Notice that the Email and Active properties were not in the JSON. When deserializing, those properties in the C# object will have their default values (null and false).

Configuration Options (JsonSerializerOptions)

By default, System.Text.Json is strict and fast. But the real world is messy. We often need to customize how JSON is generated.

For example, it’s a web standard to use camelCase (initial lowercase) for properties (userName), whereas in C# we use PascalCase (UserName).

var options = new JsonSerializerOptions
{
    WriteIndented = true, // Pretty-printed JSON (with spaces)
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase // Converts Name -> name
};

string prettyJson = JsonSerializer.Serialize(user, options);

/* Output:
{
  "id": 1,
  "name": "Luis Llamas",
  "email": "[email protected]",
  "active": true
}
*/
Copied!

Control Attributes

Sometimes we need granular control over specific properties. For that, we use attributes directly on our class.

  1. [JsonPropertyName]: For when the name in the JSON is entirely different from the C# one.
  2. [JsonIgnore]: To avoid serializing sensitive or internal data.
using System.Text.Json.Serialization;

public class Product
{
    // In the JSON it will appear as "product_id" (typical of legacy APIs)
    [JsonPropertyName("product_id")]
    public int Id { get; set; }

    public string Name { get; set; }

    // This will NEVER be saved to JSON or read from it
    [JsonIgnore]
    public double InternalCost { get; set; }
}
Copied!

XML Serialization (XmlSerializer)

Although JSON dominates the web world, XML is still very much alive in enterprise configuration and legacy systems (SOAP).

In .NET we use System.Xml.Serialization.XmlSerializer.

Special requirements for XML:

  1. The class must be public.
  2. It must have a parameterless constructor (the default constructor).
using System.Xml.Serialization;

// 1. Create the serializer specifying the type
XmlSerializer xmlSerializer = new XmlSerializer(typeof(User));

// 2. We need a Stream or Writer to write to
using (StringWriter writer = new StringWriter())
{
    xmlSerializer.Serialize(writer, user);
    string xmlOutput = writer.ToString();
    
    Console.WriteLine(xmlOutput);
}
Copied!

The output will be something like:

<?xml version="1.0" encoding="utf-16"?>
<User>
  <Id>1</Id>
  <Name>Luis Llamas</Name>
  <Email>[email protected]</Email>
  <Active>true</Active>
</User>
Copied!

XML is more verbose (takes up more space) than JSON, but it supports schemas (XSD) and is stricter with structure.

The Danger of Binary Serialization (BinaryFormatter)

Historically, C# had a class called BinaryFormatter that converted objects to pure binary. It was very convenient because it saved even private fields.

SECURITY WARNING: Microsoft has marked BinaryFormatter as obsolete and dangerous. In .NET 9, the implementation included in the runtime was removed and its methods throw exceptions.

Do not use it with untrusted data. It has serious vulnerabilities that can allow code execution if an attacker controls the serialized file.

If you need high-performance binary serialization (for example, for video games or real-time systems), use modern libraries like MessagePack or Protobuf. Another option is to use System.Text.Json and convert the text to UTF-8 bytes.