3

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)
7
  • I don't have access to source code of DLL. I cannot extend Book class so I have to use it as is. Meaning I cannot derived new Book Class MyBook from Book and start manipulating. In my ASP.net core Web API project, i have referred this third party DLL. I wanted to expose all functionality of this 3rd party DLL over http. Commented Feb 8, 2021 at 15:14
  • What if you just simply return an anonymous class, like this return Ok(new { b.Id, b.Title, b.Author, NumberOfPages = b.Pages.Count}); ? Commented Feb 8, 2021 at 15:18
  • @Peter Csala - thanks for answer but I wish Serialization should take place thorughtout application. and not just for controller. What if some other developer writing controller or just directly calls JSON Serialize method? Commented Feb 8, 2021 at 15:33
  • Could you please elaborate on this "Serialization should take place throughout application"? Commented Feb 8, 2021 at 16:10
  • 1
    I am using Newtonsoft.Json. Throughout application means wherever object is being serialized, same results should be returned. With your suggestion, I could serialize object ONLY during controller method execution . Commented Feb 8, 2021 at 16:21

2 Answers 2

8

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.

Sign up to request clarification or add additional context in comments.

5 Comments

As I said earlier, i cannot modify Book class. Book class is in 3rd party DLL.
@Atul I've posted a revised proposal, please check it.
@pater. thanks lot. I tested and working fine. Only thing is I had to move my .net core 2.2 project to 3.1 . Do you have any solution to keep my project on .net core 2.2 and still make this work?
@Atul Yepp, in case of 2.2 call AddJsonOptions instead of AddNewtonsoftJson.
Smooth. it worked. services.AddMvc().AddJsonOptions(opts => { opts.SerializerSettings.Converters.Add(new BookConverter()); }) Thanks..
2

I would take a different appoach:

  • Create simple Data Transfer Objects (DTOs) for the public API. This way you can add what properties you want and leave out those you want to hide.
  • Use Automapper to perform the tedious copying of properties between the 3rd party classes and your DTO objects.

For a better description of the idea than I'm able to write, see for example https://learn.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5

1 Comment

that is right. but i don't want to use this approach just because I am not able to serialize object to JSON. My goal is not just to make things work but I want to solve JSON serialization problem. However there several entities and their complex relation between. it will considerable efforts for me to create DTOs for all entities. instead, I want to tap only serialization of entity for specific properties that i know . Other idea is to write Custom serialization utility method explicitly and use those method to serialize object. that is what i am considering now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.