如何使用JsonSerializer反序列化异构JSON数组?
How to use JsonSerializer to deserialize a heterogenous JSON Array?
考虑以下 class:
class MethodCallDescription
{
public string MethodName { get; set; }
public object[] MethodParameters { get; set; }
}
我想将以下 JSON 反序列化为 class 的一个实例:
{
"MethodName": "LaunchRockets",
"MethodParameters": [ "Long Range", 100, true ]
}
似乎当我这样做时,MethodParameters
变成了一个包含类型 JsonElement
而不是字符串、整数和布尔对象的数组。
如何告诉 JsonSerializer 将 MethodParameters
转换为包含预期类型值的数组?
我在 System.Text.Json 源代码中遇到了 this test,它使用自定义 JsonConverter
来模仿对象反序列化的 Newtonsoft.Json 行为(它确实保持了原始对象类型相反)。
这是可用于实现此目的的自定义转换器的精简版:
class SystemObjectNewtonsoftCompatibleConverter : JsonConverter<object>
{
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.TokenType switch
{
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.Number => reader.GetInt32(),
JsonTokenType.String => reader.GetString(),
_ => Fallback(ref reader)
};
object Fallback(ref Utf8JsonReader reader)
{
// Use JsonElement as fallback.
// Newtonsoft uses JArray or JObject.
using JsonDocument document = JsonDocument.ParseValue(ref reader);
return document.RootElement.Clone();
}
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) =>
throw new InvalidOperationException("Should not get here.");
}
这个使用该转换器的示例显示了被保留的对象类型:
const string json = @"{
""MethodName"": ""LaunchRockets"",
""MethodParameters"": [ ""Long Range"", 100, true ]
}";
var options = new JsonSerializerOptions();
options.Converters.Add(new SystemObjectNewtonsoftCompatibleConverter());
MethodCallDescription instance = JsonSerializer.Deserialize<MethodCallDescription>(json, options);
Console.WriteLine(instance.MethodName);
Console.WriteLine(string.Join(", ", instance.MethodParameters.Select(p => (p, p.GetType().Name))));
提供输出:
LaunchRockets
(Long Range, String), (100, Int32), (True, Boolean)
考虑以下 class:
class MethodCallDescription
{
public string MethodName { get; set; }
public object[] MethodParameters { get; set; }
}
我想将以下 JSON 反序列化为 class 的一个实例:
{
"MethodName": "LaunchRockets",
"MethodParameters": [ "Long Range", 100, true ]
}
似乎当我这样做时,MethodParameters
变成了一个包含类型 JsonElement
而不是字符串、整数和布尔对象的数组。
如何告诉 JsonSerializer 将 MethodParameters
转换为包含预期类型值的数组?
我在 System.Text.Json 源代码中遇到了 this test,它使用自定义 JsonConverter
来模仿对象反序列化的 Newtonsoft.Json 行为(它确实保持了原始对象类型相反)。
这是可用于实现此目的的自定义转换器的精简版:
class SystemObjectNewtonsoftCompatibleConverter : JsonConverter<object>
{
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.TokenType switch
{
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.Number => reader.GetInt32(),
JsonTokenType.String => reader.GetString(),
_ => Fallback(ref reader)
};
object Fallback(ref Utf8JsonReader reader)
{
// Use JsonElement as fallback.
// Newtonsoft uses JArray or JObject.
using JsonDocument document = JsonDocument.ParseValue(ref reader);
return document.RootElement.Clone();
}
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) =>
throw new InvalidOperationException("Should not get here.");
}
这个使用该转换器的示例显示了被保留的对象类型:
const string json = @"{
""MethodName"": ""LaunchRockets"",
""MethodParameters"": [ ""Long Range"", 100, true ]
}";
var options = new JsonSerializerOptions();
options.Converters.Add(new SystemObjectNewtonsoftCompatibleConverter());
MethodCallDescription instance = JsonSerializer.Deserialize<MethodCallDescription>(json, options);
Console.WriteLine(instance.MethodName);
Console.WriteLine(string.Join(", ", instance.MethodParameters.Select(p => (p, p.GetType().Name))));
提供输出:
LaunchRockets
(Long Range, String), (100, Int32), (True, Boolean)