JSON custom serialization and deserialization of existing classes in C# asp.net core

Viewed 3421

I have existing third party DLL - which has several classes. I don't have source code of this DLL. Lets assume that it has Book class defined as below

public class Book
{ 
      public String Id { get; set; }
      public String Title { get; set; }
      public String Author{ get; set; }
      public List<Page> Pages { get; set; }
}

I have created Asp.net Core Web API project and referred DLL and wrote API Controller for GET method of http.

[HttpGet("{id}")]
public ActionResult<Book> Get(string id)
{
     Book b = Book.FindById(id); // This utility function returns Book instance.
     return Ok(b);
}

My requirement is return value of JSON should be something like

{
  "id" : "1234",
  "Title" : "How to Custom Serialize JSON",
  "Author" : "Myself",
  "NumberOfPages" : 100
}

So basically, whenever JSON serialization happens, I want to ensure that Pages attribute is not serialized as it has many complications. But I should able to add new attribute NumberOfPages. Basically I want to take control of Serialization. I have seen many examples but I could not find this particular case where I want to custom serialize existing class.

I am not worrying about deserialization right now.

I can see that serialization starts from

Microsoft.AspNetCore.Mvc.Formatters.JsonOutputFormatter.WriteObject(TextWriter writer, Object value)
2 Answers

In order to have a solution which works globally:

  1. Create a custom converter
  2. Register that converter

Create Converter

The simplest approach: create a class which derives from JsonConverter:

public class BookConverter : JsonConverter<Book>
{
    public override void WriteJson(JsonWriter writer, Book value, JsonSerializer serializer)
    {
        var bookObject = (JObject)JToken.FromObject(value);
        bookObject.Remove(nameof(value.Pages));
        bookObject.Add(new JProperty("NumberOfPages", value.Pages.Count));
        bookObject.WriteTo(writer);
    }

    public override Book ReadJson(JsonReader reader, Type objectType, Book existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        return new Book(); //TODO: fix me, now it is enough to make it compile
    }
}
  • Here I have converted the Book instance (value) to a JObject.
  • I have ignored the Pages property by calling the Remove.
  • I have extended the output with NumberOfPages by calling the Add.
  • I've skipped the ReadJson part, because it is out of scope for this question.

Register Converter

.NET Core 2.x

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddControllers()
        .AddJsonOptions(opts =>
    {
        opts.SerializerSettings.Converters.Add(new BookConverter());
    });
}

.NET Core 3.x or .NET 5.x

In case of ASP.NET Core 3.1 or above the default json serializer is the System.Text.Json. In order to use Json.Net you need to install the Microsoft.AspNetCore.Mvc.NewtonsoftJson package. This exposes a method called AddNewtonsoftJson where you can register the custom converter:

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddControllers()
        .AddNewtonsoftJson(opts =>
    {
        opts.SerializerSettings.Converters.Add(new BookConverter());
    });
}

With this approach all Book instances will be converted into the desired format when ASP.NET Core calling the serializer.

Related