How to write custom converters for JSON serialization in .NET
What is a JSON Convertor
A JSON converter is a class designed to facilitate the conversion of objects or values to and from JSON format.
In the context of working with APIs, there are frequent scenarios where the transformation of values received from HTTP clients into our preferred data types becomes necessary. In such situations convertors can play a vital role.
Custom JSON Conversion Scenarios
There are no hard code rule for this thing but here are few cases when I find making convertors helpful :
1/ Enum Type Handling
2/ Custom Date Formatting
3/ Localization of Date and Number Formats
How to create JSON Convertor in .NET
I am fan of System.Text.Json so I would demonstrate how to create convertors using that.
To make it more understandable let’s make a scenario, suppose we have a property in our response model named Status Code which is returned from API we are calling.
Problem is it could be int and string both we are not sure what will come. One could suggest that let’s use dynamic data type.
But I don’t like it, I will go for a JSON convertor and make data type of StatusCode as string. So my convertor would look like this.
public sealed class CustomStringConverter : JsonConverter<string>
{
// Reads JSON and converts it to a string
public override string Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetInt32(out int number))
{
return number.ToString();
}
}
// Read the string value from the JSON reader
string? value = reader.GetString();
// Return the string value or an empty string if null
return value ?? string.Empty;
}
// Writes a string value to JSON
public override void Write(
Utf8JsonWriter writer,
string value,
JsonSerializerOptions options)
{
// Write the string value to the JSON writer
writer.WriteStringValue(value);
}
}
If you don’t like the example pardon, just focus on understanding the concept. Now we can apply this convertor like this :
[JsonPropertyName("StatusCode")]
[JsonConverter(typeof(StringConverter))]
public string? StatusCode { get; set; }
What exactly is happening in convertor code ?
Read Method:
The Read method is responsible for converting JSON data into a string when your program is reading or deserializing JSON.
Write Method:
The Write method is responsible for converting a string into the appropriate JSON format when your program is writing or serializing JSON
Another converter that overrides default serialization for an existing data type. The converter uses mm/dd/yyyy format for DateTimeOffset properties.
public sealed class DateTimeOffsetJsonConverter : JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
DateTimeOffset.ParseExact(reader.GetString()!,
"MM/dd/yyyy", CultureInfo.InvariantCulture);
public override void Write(
Utf8JsonWriter writer,
DateTimeOffset dateTimeValue,
JsonSerializerOptions options) =>
writer.WriteStringValue(dateTimeValue.ToString(
"MM/dd/yyyy", CultureInfo.InvariantCulture));
}
Convertors that accepts Parameters + Factory:
Sometime we need to send parameters to our convertor, you might be thinking how can we handle it in convertors. Quite simple follow these steps :
Create convertors for each scenerio like we did in previous step.
Now create another class which acts like a factory, this class will implement JsonConverterAttribute
public sealed class AttributedConverter : JsonConverterAttribute
{
public AttributedConverter(int someParameter)
{
_someParameter = someParameter;
}
public int _someParameter { get; private set; }
public override JsonConverter? CreateConverter(Type typeToConvert)
{
// Now decide on the basis of typeToConvert
// which converter you want to call.
}
}
Utilise reflection here, check the types with typeOf and call your desired convertors.
In some cases convertors don’t work by just putting it over the property, we have to add it in serialization options. I mostly work with Refit and I don’t find need of adding it. If due to some reasons it does not work one can add it like this :
public static JsonSerializerOptions DefaultOptions => new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters =
{
// Here goes your converters
}
};
Now one can use these options for serialization/de-serialization.
Summary
A JSON converter is a specialized class designed to handle the conversion of objects or values to and from JSON format
Common scenarios where custom JSON converters are beneficial include handling Enum types, implementing custom date formatting, and localizing date and number formats.
In the .NET , using System.Text.Json, creating a JSON converter involves defining a class that overrides default serialization or deserialization for a specific data type.
The Read method within the converter is responsible for deserializing JSON and write for Serializing
For cases where converters require parameters, a factory class is introduced, implementing the JsonConverterAttribute, and the desired parameters are passed through the constructor of the converter class.
In some situations, converters may need to be explicitly added to serialization options. This can be achieved by configuring serialization options with the custom converter, allowing for effective customization of serialization and deserialization processes.
Whenever you're ready, there are 3 ways I can help you:
- Subscribe to my youtube channel : For in-depth tutorials, coding tips, and industry insights.
- Promote yourself to 9,000+ subscribers : By sponsoring this newsletter
- Patreon community : Get access to all of my blogs and articles at one place