4

Im trying to migrate from json.net to microsoft's json and found something that behaves very differently.

Let's use this simplified example:

public interface IName
{
    string Name { get; set; }

}

public class Person : IName
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public void Foo() 
{
   IName p = new Person {Age = 4, Name = "Waldo"};
   var s1 = System.Text.Json.JsonSerializer.Serialize(p); // --> {"Name":"Waldo"}
   var s2 = Newtonsoft.Json.JsonConvert.SerializeObject(p); // --> {"Name":"Waldo","Age":4}
}

Microsoft's Serializers serializes properties from IName JSON.NET serializes properties from Person

Is there a way to configure it so that it would work like JSON.NET? The options that I could pass do not indicate that this is configurable. Did I overlook something?

dbc
  • 104,963
  • 20
  • 228
  • 340
gsharp
  • 27,557
  • 22
  • 88
  • 134
  • Check the logged issue --- https://github.com/dotnet/corefx/issues/37613. Currently only way is to deserialize via `var s1 = System.Text.Json.JsonSerializer.Serialize(p, p.GetType());` – user1672994 Oct 17 '19 at 09:39

4 Answers4

5

This is because the serializer uses the type of the generic parameter, not the type of the passed value:

public static string Serialize<TValue>(TValue value, JsonSerializerOptions options = null)
{
    return WriteCoreString(value, typeof(TValue), options);
}

This passes typeof(IName) to WriteCoreString, and on that type ultimately reflection is performed.

You can work around this by explicitly passing the type to the overload that accepts that:

var s3 = System.Text.Json.JsonSerializer.Serialize(p, p.GetType());

This returns:

{"Name":"Waldo","Age":4}

Casting to object also works, as the code then calls value.GetType():

var s4 = System.Text.Json.JsonSerializer.Serialize((object)p);
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
2

Please see Serialize properties of derived classes

Serialization of a polymorphic type hierarchy is not supported. For example, if a property is defined as an interface or an abstract class, only the properties defined on the interface or abstract class are serialized, even if the runtime type has additional properties. The exceptions to this behavior are explained in this section.

(Examples here)

This behavior is intended to help prevent accidental exposure of data in a derived runtime-created type.

And then:

To serialize the properties of the derived type, use one of the following approaches:

  • Call an overload of Serialize that lets you specify the type at runtime:

    json = JsonSerializer.Serialize(weatherForecast, weatherForecast.GetType());

  • Declare the object to be serialized as object.

    json = JsonSerializer.Serialize<object>(weatherForecast);

tymtam
  • 31,798
  • 8
  • 86
  • 126
2

Newtonsoft.Json.JsonConvert.SerializeObject is a non-generic method so that it analyzes the provided object at runtime and serializes all properties that the object has.

In comparison System.Text.Json.JsonSerializer.Serialize(p) is resolved to a generic method. The compiler infers the type parameters based upon the type of the variable, in the case of your same IName. Therefore, the method analyzes the provided type and exports the properties of the generic type parameter, not all properties of the object that implements the interface.

The documentation shows that the serialize method always needs to be given a type, either through the generic type parameter or as a parameter to the method.

The following code should fixes the behavior:

var s1 = System.Text.Json.JsonSerializer.Serialize(p, p.GetType()); 
// --> {"Name":"Waldo","Age":4}

See this sample.

Markus
  • 20,838
  • 4
  • 31
  • 55
0

I think what you need is to pass the reference of the object to the method

        IName p = new Person { Age = 4, Name = "Waldo" };
        var s1 = System.Text.Json.JsonSerializer.Serialize<Person>((Person)p);

For .NET Core 3.0

Ahmed Msaouri
  • 316
  • 1
  • 10