使用 System.Text.Json 序列化和反序列化包含复杂属性的对象
Serialize and Deserialize objects that contain complex properties using System.Text.Json
假设我有这些 classes:
// serialize this enum value as string
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
enum AnimalType
{
Dog,
Cat
}
abstract class Animal
{
public abstract AnimalType AnimalType { get; }
}
class Dog : Animal
{
public override AnimalType AnimalType => AnimalType.Dog;
}
class Cat : Animal
{
public override AnimalType AnimalType => AnimalType.Cat;
}
class Person
{
public Animal Pet { get; set; }
}
然后我将能够序列化一个 Person 但我将无法反序列化它:
var person = new Person();
person.Pet = new Dog();
// this works. It outputs: {"Pet":{"AnimalType":"Dog"}}
var json = System.Text.Json.JsonSerializer.Serialize(person);
// this does not work! and it makes sense
var clone = System.Text.Json.JsonSerializer.Deserialize<Person>(json);
为了使前面的代码正常工作,我必须创建这个 class:
class AnimalConverter : System.Text.Json.Serialization.JsonConverter<Animal>
{
public override Animal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// I am assuming the animal is a dog
return System.Text.Json.JsonSerializer.Deserialize<Dog>(ref reader);
}
public override void Write(Utf8JsonWriter writer, Animal value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
现在这段代码可以工作了:
var person = new Person();
person.Pet = new Dog();
// I am able to serialize
var json = System.Text.Json.JsonSerializer.Serialize(person);
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new AnimalConverter());
// now this works but only if Person has a Dog
var clone = System.Text.Json.JsonSerializer.Deserialize<Person>(json, options);
如您所见,此代码仅在该人将狗作为宠物时才有效。
- 我怎样才能让它变得聪明,让它知道如何反序列化?我试过玩
Utf8JsonReader reader
但我无法寻找和改变位置。看来我唯一的选择就是手动解析整个文档。
如果我可以执行一个 peek 操作来查看它是什么类型的对象,然后根据它反序列化它,那就太好了
编辑
问题用更简单的方式解释:
我有一个 asp.net 网站 API,用户向我发送 json 对象:
{"Pet":{"AnimalType":"Dog"}}
如何将其反序列化为正确的 C# 对象?
终于能够使用 Newtonsoft 而不是 System.Text.Json 来解决它。
我必须添加一些 属性 来帮助我识别 class。所以我创建了这个枚举:
public enum AnimalType
{
Dog,
Cat
}
现在我的 class 看起来像这样:
public abstract class Animal
{
//[JsonConverter(typeof(CustomConverter))]
public abstract AnimalType AnimalType { get; }
public string? AnimalName { get; set; }
}
public class Dog : Animal
{
public override AnimalType AnimalType => AnimalType.Dog;
public string DogName { get; set; } = string.Empty;
}
public class Cat : Animal
{
public override AnimalType AnimalType => AnimalType.Cat;
public string CatName { get; set; } = string.Empty;
}
public class Person
{
public Animal Pet { get; set; }
}
这个转换器使我能够反序列化一个人:
public class AnimalConverter : JsonConverter<Animal>
{
public override Animal? ReadJson(JsonReader reader, Type objectType, Animal? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
if (jObject is null)
return null;
var animalType = jObject.GetValue("AnimalType", StringComparison.OrdinalIgnoreCase)?.Value<string>();
if (animalType == null)
throw new Exception();
var animalTypeValue = Enum.Parse<AnimalType>(animalType);
switch (animalTypeValue)
{
case AnimalType.Dog:
return jObject.ToObject<Dog>();
case AnimalType.Cat:
return jObject.ToObject<Cat>();
default:
break;
}
throw new NotImplementedException();
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, Animal? value, JsonSerializer serializer)
{
throw new Exception("This should never be called because CanWrite is set to false");
}
}
确保您指定在 asp.net 核心网络应用程序上使用该转换器作为:
// use this converters
var mvcBuilder = builder.Services.AddControllers().AddNewtonsoftJson(c =>
{
c.SerializerSettings.Converters.Add(new StringEnumConverter());
c.SerializerSettings.Converters.Add(new AnimalConverter());
});
并使用这个 NuGet 包:
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.3.0" />
</ItemGroup>
假设我有这些 classes:
// serialize this enum value as string
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
enum AnimalType
{
Dog,
Cat
}
abstract class Animal
{
public abstract AnimalType AnimalType { get; }
}
class Dog : Animal
{
public override AnimalType AnimalType => AnimalType.Dog;
}
class Cat : Animal
{
public override AnimalType AnimalType => AnimalType.Cat;
}
class Person
{
public Animal Pet { get; set; }
}
然后我将能够序列化一个 Person 但我将无法反序列化它:
var person = new Person();
person.Pet = new Dog();
// this works. It outputs: {"Pet":{"AnimalType":"Dog"}}
var json = System.Text.Json.JsonSerializer.Serialize(person);
// this does not work! and it makes sense
var clone = System.Text.Json.JsonSerializer.Deserialize<Person>(json);
为了使前面的代码正常工作,我必须创建这个 class:
class AnimalConverter : System.Text.Json.Serialization.JsonConverter<Animal>
{
public override Animal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// I am assuming the animal is a dog
return System.Text.Json.JsonSerializer.Deserialize<Dog>(ref reader);
}
public override void Write(Utf8JsonWriter writer, Animal value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
现在这段代码可以工作了:
var person = new Person();
person.Pet = new Dog();
// I am able to serialize
var json = System.Text.Json.JsonSerializer.Serialize(person);
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new AnimalConverter());
// now this works but only if Person has a Dog
var clone = System.Text.Json.JsonSerializer.Deserialize<Person>(json, options);
如您所见,此代码仅在该人将狗作为宠物时才有效。
- 我怎样才能让它变得聪明,让它知道如何反序列化?我试过玩
Utf8JsonReader reader
但我无法寻找和改变位置。看来我唯一的选择就是手动解析整个文档。
如果我可以执行一个 peek 操作来查看它是什么类型的对象,然后根据它反序列化它,那就太好了
编辑
问题用更简单的方式解释:
我有一个 asp.net 网站 API,用户向我发送 json 对象:
{"Pet":{"AnimalType":"Dog"}}
如何将其反序列化为正确的 C# 对象?
终于能够使用 Newtonsoft 而不是 System.Text.Json 来解决它。
我必须添加一些 属性 来帮助我识别 class。所以我创建了这个枚举:
public enum AnimalType
{
Dog,
Cat
}
现在我的 class 看起来像这样:
public abstract class Animal
{
//[JsonConverter(typeof(CustomConverter))]
public abstract AnimalType AnimalType { get; }
public string? AnimalName { get; set; }
}
public class Dog : Animal
{
public override AnimalType AnimalType => AnimalType.Dog;
public string DogName { get; set; } = string.Empty;
}
public class Cat : Animal
{
public override AnimalType AnimalType => AnimalType.Cat;
public string CatName { get; set; } = string.Empty;
}
public class Person
{
public Animal Pet { get; set; }
}
这个转换器使我能够反序列化一个人:
public class AnimalConverter : JsonConverter<Animal>
{
public override Animal? ReadJson(JsonReader reader, Type objectType, Animal? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
if (jObject is null)
return null;
var animalType = jObject.GetValue("AnimalType", StringComparison.OrdinalIgnoreCase)?.Value<string>();
if (animalType == null)
throw new Exception();
var animalTypeValue = Enum.Parse<AnimalType>(animalType);
switch (animalTypeValue)
{
case AnimalType.Dog:
return jObject.ToObject<Dog>();
case AnimalType.Cat:
return jObject.ToObject<Cat>();
default:
break;
}
throw new NotImplementedException();
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, Animal? value, JsonSerializer serializer)
{
throw new Exception("This should never be called because CanWrite is set to false");
}
}
确保您指定在 asp.net 核心网络应用程序上使用该转换器作为:
// use this converters
var mvcBuilder = builder.Services.AddControllers().AddNewtonsoftJson(c =>
{
c.SerializerSettings.Converters.Add(new StringEnumConverter());
c.SerializerSettings.Converters.Add(new AnimalConverter());
});
并使用这个 NuGet 包:
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.3.0" />
</ItemGroup>