当将特定的 class 转换为其他内容时,JsonSerializer 的行为不符合预期

JsonSerializer behaves not as expected when the specifc class is casted to something else

我正在尝试从 json.net 迁移到 Microsoft 的 json 并发现一些行为非常不同。

让我们使用这个简化的例子:

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 的序列化程序从 IName 序列化属性 JSON.NET 序列化 Person

的属性

有没有办法配置它,使其像 JSON.NET 一样工作?我可以传递的选项并不表示这是可配置的。我忽略了什么吗?

这是因为序列化器uses the type of the generic parameter,不是传值的类型:

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

这会将 typeof(IName) 传递给 WriteCoreString,并最终在该类型上执行反射。

您可以通过 explicitly passing the type 接受以下内容的重载来解决此问题:

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

这个returns:

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

转换为 object 也可以,因为代码 then calls value.GetType():

var s4 = System.Text.Json.JsonSerializer.Serialize((object)p);

我认为你需要的是将对象的引用传递给方法

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

对于 .NET Core 3.0

请参阅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.

然后:

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);

Newtonsoft.Json.JsonConvert.SerializeObject 是一种非泛型方法,因此它会在运行时分析提供的对象并序列化该对象具有的所有属性。

相比之下 System.Text.Json.JsonSerializer.Serialize(p) 被解析为 generic method。在相同的 IName 的情况下,编译器根据变量的类型推断类型参数。因此,该方法分析提供的类型并导出泛型类型参数的属性,而不是实现接口的对象的所有属性。

文档显示序列化方法总是需要通过泛型类型参数或作为方法的参数来指定类型。

以下代码应该可以修复该行为:

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

看到这个sample