Logo

dev-resources.site

for different kinds of informations.

Mapping > Json Converters // true

Published at
1/31/2023
Categories
dotnet
csharp
serialization
Author
akhansari
Categories
3 categories in total
dotnet
open
csharp
open
serialization
open
Author
9 person written this
akhansari
open
Mapping > Json Converters // true

Let's imagine that we have a cool domain with some special types.
For instance, in our very simple example, we have a strongly-typed ID to get rid of primitive obsession.

public record struct CoolId(int Value);

public class Cool
{
  public CoolId CoolId { get; set; }
  public string Title { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Nice! OK, now let's create an API for this.
Amazing how easy ASP.NET Core is.

var builder = WebApplication.CreateBuilder();

builder.Services.Configure<JsonOptions>(o =>
{
  o.SerializerOptions.WriteIndented = true;
});

var app = builder.Build();

app.MapGet("/cool", () =>
  new Cool { CoolId = new CoolId(123), Title = "Very Cool!" });

app.Run();
Enter fullscreen mode Exit fullscreen mode

The concern is that CoolId is not correctly serialized and it should be simplified.

{
  "coolId": {
    "value": 123
  },
  "title": "Very Cool!"
}
Enter fullscreen mode Exit fullscreen mode

So, the first reaction is to create a JsonConverter for it.
Hmm, why not.

public class CoolIdJsonConverter : JsonConverter<CoolId>
{
  public override CoolId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
    new CoolId(reader.GetInt32());

  public override void Write(Utf8JsonWriter writer, CoolId value, JsonSerializerOptions options) =>
    writer.WriteNumberValue(value.Value);
}
Enter fullscreen mode Exit fullscreen mode
builder.Services.Configure<JsonOptions>(o =>
{
  o.SerializerOptions.WriteIndented = true;
  o.SerializerOptions.Converters.Add(new CoolIdJsonConverter());
});
Enter fullscreen mode Exit fullscreen mode

And voila!

{
  "coolId": 123,
  "title": "Very Cool!"
}
Enter fullscreen mode Exit fullscreen mode

Drawbacks

The big issue with this approach and custom serializers in general is that the entire logic is hidden in the wilds of the serializer library.
This leads to the following problems:

  • Very bad model discovery. It's pretty hard to read and guess the model from the custom serializer. Especially in big payloads when the construction depends on many logics.
  • Highly coupled with the serializer library.
    • Not compatible with OpenAPI/Swagger generation.
    • Logic duplication if we want to provide other formats such as Xml or Yaml.
    • Main blocking point when we want to switch to another library. For instance Json.Net to System.Text.Json.
    • Not convenient to test raw strings.
  • Reflection hell.
  • We usually end up creating a client library (NuGet) for the API which is a bad practice and the opposite of the OpenAPI and Restful philosophy.

Mapping > Custom Serialization

With a simple mapping we can fix all the previous drawbacks.

public class CoolModel
{
  public int CoolId { get; set; }
  public string Title { get; set; }
}

public static class CoolModelMapper
{
  public static Cool ToDomain(this CoolModel model) => new Cool
  {
    CoolId = new CoolId(model.CoolId),
    Title = model.Title,
  };

  public static CoolModel ToModel(this Cool domain) => new CoolModel
  {
    CoolId = domain.CoolId.Value,
    Title = domain.Title,
  };
}
Enter fullscreen mode Exit fullscreen mode

What is handy with this pattern is the possibility to make a composition of ToModel or ToDomain. For instance, we can replace domain.CoolId.Value with domain.CoolId.ToModel() and so forth.

app.MapGet("/", () =>
    new Cool { CoolId = new CoolId(123), Title = "Very Cool!" }
    .ToModel());
Enter fullscreen mode Exit fullscreen mode

Conclusion

JSON is simple, and so should its model.

The examples were in C# but it's also true for F# or any other languages.

serialization Article's
30 articles in total
Favicon
Java interacting with Apache Avro
Favicon
Apache Avro
Favicon
Protocol Buffers as a Serialization Format
Favicon
WSON (Web Simple Object Notation)
Favicon
Serializing Python Object Using the pickle Module
Favicon
Converting a Unicorn project to Sitecore Content Serialization (SCS)
Favicon
Converting a TDS project to Sitecore Content Serialization (SCS) 
Favicon
Deserializing Polymorphic Lists with Kotlin, Jackson, and Spring Boot
Favicon
Mapify-ts
Favicon
ProtoBuf message serialization in Akka.NET using protobuf-net
Favicon
Pandas Dataframe to AVRO
Favicon
Mapping > Json Converters // true
Favicon
Nested Encoding Efficency
Favicon
Java serialization with Avro
Favicon
Java Serialization with Flatbuffers
Favicon
Java Serialization with Protocol Buffers
Favicon
Serialização de Objectos
Favicon
Effective Java: Consider Serialization Proxies Instead of Serialized Instances
Favicon
Effective Java: For Instance Control, Prefer Enum types to readResolve
Favicon
Effective Java: Write readObject Methods Defensively
Favicon
Effective Java: Consider Using a Custom Serialized Form
Favicon
Effective Java: Prefer Alternatives To Java Serialization
Favicon
ASP.NET XML serialisation issues: Observations on DataContractSerializer
Favicon
ReScript JSON Typed Strongly
Favicon
使用序列化在兩個 Rails 站台間傳遞物件
Favicon
Serialização no Kotlin
Favicon
Working with Firebase Cloud Firestore made easier with "withConverter()"
Favicon
Meet Model Object Mapper, a Database Serialization Utility for Django!
Favicon
How to Speak Binary in TypeScript
Favicon
Practical Java 16 - Using Jackson to serialize Records

Featured ones: