System.Text.Json – Using anonymous types to customize serialization

It’s common to need to customize serialization. When you need to do this, you’d typically need to create a custom converter and pass it in during serialization. Depending on your scenario, an alternative approach is to use anonymous types, like this:

var json = JsonSerializer.Serialize(new { book.Title, book.Author });
Code language: C# (cs)

Basically you select properties from another object and format them as desired, and then serialize it. If you need to deserialize the JSON created from the anonymous type, you can deserialize to a dynamic object.

In this article, I’ll show a few cases where you could use the anonymous type approach to customize serialization. If you happen to find a new use case for this approach, feel free to leave a comment.

Use case 1 – Formatting before serializing

When you want to change the format of a property during serialization, normally you’d have to create a custom converter. In some cases, it may be simpler to select the property into an anonymous type, format it as desired, and serialize it.

For example, let’s say you want to serialize a DateTimeOffset property and only want to display the time part of it. Here’s how you could do that:

var message = new Message() { Text = "I want to go see the Old movie", From = "Mak", SentAt = DateTimeOffset.Now }; var json = JsonSerializer.Serialize(new { message.From, message.Text, SentAt = message.SentAt.ToString("hh:mm:ss") }, new JsonSerializerOptions() { WriteIndented = true }); Console.WriteLine(json);
Code language: C# (cs)

This outputs the following JSON:

{ "From": "Mak", "Text": "I want to go see the Old movie", "SentAt": "07:46:01" }
Code language: JSON / JSON with Comments (json)

Use case 2 – Serializing a subset of properties

Sometimes you’ll only want to serialize some of the properties instead of all of them. You can’t really use the [JsonIgnore] attribute in this scenario, because then the ignored properties would always be ignored. Instead, you can select the desired properties into an anonymous type and serialize it.

For example, let’s say you’re using the following logger that accepts an object parameter (for context purposes) that it’ll serialize into JSON:

public class JsonLogger { public void Info(string message, object forContext); }
Code language: C# (cs)

When you load a book from the database, you want to log that you loaded it, and you want to log the book title/author for context. To do that, you’d select the Title and Author properties into an anonymous type:

var book = GetBookFromDatabase(isbn: "9780679604181"); jsonLogger.Info("Loaded book from database", new { book.Title, book.Author });
Code language: C# (cs)

The logger serializes the context object into JSON and logs the following:

message=Loaded book from database context={"Title":"The Black Swan: The Impact of the Highly Improbable","Author":"Nassim Nicholas Taleb"}
Code language: plaintext (plaintext)

Use case 3 – Changing property names to what the client expects

Let’s say your property names are in English and one of your clients expects the properties to be in Spanish.

You can’t use the JsonPropertyName attribute, because that would change the serialized property name every time you serialize. Instead, you can adapt your object’s properties to what the client wants by selecting them into an anonymous type with different names, and then serializing it, like this:

var celebrity = new Celebrity() { BirthDate = new DateTime(year: 1967, month: 2, day: 19), FirstName = "Benicio", LastName = "del Toro Sanchez" }; var json = JsonSerializer.Serialize(new { nombre = celebrity.FirstName, apellidos = celebrity.LastName, fechaDeNacimiento = celebrity.BirthDate.ToShortDateString() }, new JsonSerializerOptions() { WriteIndented = true }); Console.WriteLine(json);
Code language: C# (cs)

This outputs the following:

{ "nombre": "Benicio", "apellidos": "del Toro Sanchez", "fechaDeNacimiento": "2/19/1967" }
Code language: JSON / JSON with Comments (json)

Note: This approach is known as the adapter pattern. It’s just not done in the traditional way where you add a new adapter class.

Note 2: In the Spanish naming system, there are two last names (apellidos). Usually only the first last name is shown. In the example above, ‘del Toro’ is the first last name, and he’s usually referred to as Benicio del Toro.

Use case 4 – Serializing internal properties

By default, JsonSerializer only serializes public properties. What if you want to serialize a non-public property without using a custom converter?

For example, let’s say you have the following class with an internal property called HappenedAt:

public class SystemEvent { public string Name { get; set; } internal DateTimeOffset HappenedAt { get; set; } public SystemEvent() { HappenedAt = DateTimeOffset.Now; } }
Code language: C# (cs)

To serialize the internal HappenedAt property, you could select its value into an anonymous type.

If the code has access to the internal property, you can select it directly into an anonymous type:

var sysEvent = new SystemEvent() { HappenedAt = DateTimeOffset.Now, Name = "Detected a disturbance in the force" }; var json = JsonSerializer.Serialize(new { sysEvent.Name, sysEvent.HappenedAt }, new JsonSerializerOptions() { WriteIndented = true }); Console.WriteLine(json);
Code language: C# (cs)

If the code doesn’t have access to the internal property (the class is defined in a different assembly), then you can use reflection to get the property value and select it into the anonymous type:

var sysEvent = new SystemEvent() { Name = "Detected a disturbance in the force" }; var json = JsonSerializer.Serialize(new { sysEvent.Name, HappenedAt = typeof(SystemEvent).GetProperty("HappenedAt", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(sysEvent) }, new JsonSerializerOptions() { WriteIndented = true }); Console.WriteLine(json);
Code language: C# (cs)

Both approaches output the same JSON:

{ "Name": "Detected a disturbance in the force", "HappenedAt": "2021-07-16T08:10:31.3865104-04:00" }
Code language: JSON / JSON with Comments (json)

Leave a Comment