Writing a custom deserializer in .NET 5

One of the things I miss from my XAML development days are the converters. A converter was just what the name says, a class that converts a property to something that you can show in your UI. For example, maybe you had a timestamp in your model and instead of showing 1616623541 in your UI, you could convert it to "25th of March 2021". Today I learned that I can do something like this in my dotnet controllers using custom de/serializers.

Let's say you have the following requirement for an endpoint:

  • receive an image encoded in base64 along with other information, as a JSON payload
  • convert the base64 image to bytes in order to save it or send it somewhere else

Usually, you would make a class that looks like the one below and you would convert the base64 string to an array of bytes.

public class UploadImageModel
{
    public string Image { get; set; }

    public string SomeOtherProperty { get; set; }
}

[HttpPost]
public async Task<IActionResult> Post([FromBody] UploadImageModel model)
{
    byte[] imageBytes = Convert.FromBase64String(model.Image);
    // now do something with the bytes array
    await _myOtherAPI.UploadImageBytes(imageBytes);
}

With a custom deserializer you don't have to do this manually because you can just add an attribute to any property from your model, and it will be done automatically:

public class UploadImageModel
{
    [JsonConverter(typeof(Base64FileJsonConverter))]
    public byte[] Image { get; set; }

    public string SomeOtherProperty { get; set; }
}

public class Base64FileJsonConverter : JsonConverter<byte[]>
{
    public override byte[] Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options)
    {
        return reader.GetBytesFromBase64();
    }

    public override void Write(Utf8JsonWriter writer, byte[] wf, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

[HttpPost]
public async Task<IActionResult> Post([FromBody] UploadImageModel model)
{
    // now we use our Image property directly
    await _myOtherAPI.UploadImageBytes(model.Image);
}

Notice that now we have a byte[] Image property instead of a string, and the Base64FileJsonConverter converter is converting the base64 string into a byte array. That byte array will be assigned to our Image property so we don't have to make the conversion in our controller.

I also made a small repo where you can play with the code. It has a simple controller where you can POST a JSON with a base64 encoded image and it will return the image back. Enjoy!

image.png

Did you find this article valuable?

Support Bogdan Bujdea by becoming a sponsor. Any amount is appreciated!