C# - 将嵌套 json 反序列化为嵌套字典 <string, object>
C# - Deserializing nested json to nested Dictionary<string, object>
我正在使用 .net core 3.1 和库 System.Text.Json
如何将嵌套的 json 对象反序列化为 Dictionary,但期望基于 json 属性 类型我将获得正确的 C# 类型:
String -> string
Number -> int/double
Object -> Dictionary<string, object>
默认情况下 - 如果我尝试反序列化为 Dictionary - 基本上每个对象都是一个 JsonElement。
我希望它是上面提到的类型。
知道如何实现吗?
为了将自由格式 JSON 反序列化为 .Net 基本类型而不是 JsonElement
对象,您需要编写一个 custom JsonConverter
,因为没有提供这样的功能System.Text.Json
开箱即用。
一个这样的转换器如下:
public class ObjectAsPrimitiveConverter : JsonConverter<object>
{
FloatFormat FloatFormat { get; init; }
UnknownNumberFormat UnknownNumberFormat { get; init; }
ObjectFormat ObjectFormat { get; init; }
public ObjectAsPrimitiveConverter() : this(FloatFormat.Double, UnknownNumberFormat.Error, ObjectFormat.Expando) { }
public ObjectAsPrimitiveConverter(FloatFormat floatFormat, UnknownNumberFormat unknownNumberFormat, ObjectFormat objectFormat)
{
this.FloatFormat = floatFormat;
this.UnknownNumberFormat = unknownNumberFormat;
this.ObjectFormat = objectFormat;
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
if (value.GetType() == typeof(object))
{
writer.WriteStartObject();
writer.WriteEndObject();
}
else
{
JsonSerializer.Serialize(writer, value, value.GetType(), options);
}
}
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.Null:
return null;
case JsonTokenType.False:
return false;
case JsonTokenType.True:
return true;
case JsonTokenType.String:
return reader.GetString();
case JsonTokenType.Number:
{
if (reader.TryGetInt32(out var i))
return i;
if (reader.TryGetInt64(out var l))
return l;
// BigInteger could be added here.
if (FloatFormat == FloatFormat.Decimal && reader.TryGetDecimal(out var m))
return m;
else if (FloatFormat == FloatFormat.Double && reader.TryGetDouble(out var d))
return d;
using var doc = JsonDocument.ParseValue(ref reader);
if (UnknownNumberFormat == UnknownNumberFormat.JsonElement)
return doc.RootElement.Clone();
throw new JsonException(string.Format("Cannot parse number {0}", doc.RootElement.ToString()));
}
case JsonTokenType.StartArray:
{
var list = new List<object>();
while (reader.Read())
{
switch (reader.TokenType)
{
default:
list.Add(Read(ref reader, typeof(object), options));
break;
case JsonTokenType.EndArray:
return list;
}
}
throw new JsonException();
}
case JsonTokenType.StartObject:
var dict = CreateDictionary();
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.EndObject:
return dict;
case JsonTokenType.PropertyName:
var key = reader.GetString();
reader.Read();
dict.Add(key, Read(ref reader, typeof(object), options));
break;
default:
throw new JsonException();
}
}
throw new JsonException();
default:
throw new JsonException(string.Format("Unknown token {0}", reader.TokenType));
}
}
protected virtual IDictionary<string, object> CreateDictionary() =>
ObjectFormat == ObjectFormat.Expando ? new ExpandoObject() : new Dictionary<string, object>();
}
public enum FloatFormat
{
Double,
Decimal,
}
public enum UnknownNumberFormat
{
Error,
JsonElement,
}
public enum ObjectFormat
{
Expando,
Dictionary,
}
要使用它,请反序列化为 object
(或 dynamic
,如果配置为使用 ExpandoObject
),如下所示:
var options = new JsonSerializerOptions
{
Converters = { new ObjectAsPrimitiveConverter(floatFormat : FloatFormat.Double, unknownNumberFormat : UnknownNumberFormat.Error, objectFormat : ObjectFormat.Expando) },
WriteIndented = true,
};
dynamic d = JsonSerializer.Deserialize<dynamic>(json, options);
备注:
JSON 允许任意精度和大小的数字,而 .Net 原始数字类型则不允许。在无法将某些 JSON 数字解析为 .Net 原始类型的情况下,转换器提供了选择 return 数字的 JsonElement
或抛出异常的选项。
可以扩展转换器以尝试将不支持的数字反序列化为 BigInteger
。
您可以将转换器配置为对浮点数使用 double
或 decimal
,对 [=50= 使用 Dictionary<string, object>
或 ExpandoObject
] 对象。
演示 fiddle here.
我正在使用 .net core 3.1 和库 System.Text.Json
如何将嵌套的 json 对象反序列化为 Dictionary
String -> string
Number -> int/double
Object -> Dictionary<string, object>
默认情况下 - 如果我尝试反序列化为 Dictionary
知道如何实现吗?
为了将自由格式 JSON 反序列化为 .Net 基本类型而不是 JsonElement
对象,您需要编写一个 custom JsonConverter
,因为没有提供这样的功能System.Text.Json
开箱即用。
一个这样的转换器如下:
public class ObjectAsPrimitiveConverter : JsonConverter<object>
{
FloatFormat FloatFormat { get; init; }
UnknownNumberFormat UnknownNumberFormat { get; init; }
ObjectFormat ObjectFormat { get; init; }
public ObjectAsPrimitiveConverter() : this(FloatFormat.Double, UnknownNumberFormat.Error, ObjectFormat.Expando) { }
public ObjectAsPrimitiveConverter(FloatFormat floatFormat, UnknownNumberFormat unknownNumberFormat, ObjectFormat objectFormat)
{
this.FloatFormat = floatFormat;
this.UnknownNumberFormat = unknownNumberFormat;
this.ObjectFormat = objectFormat;
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
if (value.GetType() == typeof(object))
{
writer.WriteStartObject();
writer.WriteEndObject();
}
else
{
JsonSerializer.Serialize(writer, value, value.GetType(), options);
}
}
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.Null:
return null;
case JsonTokenType.False:
return false;
case JsonTokenType.True:
return true;
case JsonTokenType.String:
return reader.GetString();
case JsonTokenType.Number:
{
if (reader.TryGetInt32(out var i))
return i;
if (reader.TryGetInt64(out var l))
return l;
// BigInteger could be added here.
if (FloatFormat == FloatFormat.Decimal && reader.TryGetDecimal(out var m))
return m;
else if (FloatFormat == FloatFormat.Double && reader.TryGetDouble(out var d))
return d;
using var doc = JsonDocument.ParseValue(ref reader);
if (UnknownNumberFormat == UnknownNumberFormat.JsonElement)
return doc.RootElement.Clone();
throw new JsonException(string.Format("Cannot parse number {0}", doc.RootElement.ToString()));
}
case JsonTokenType.StartArray:
{
var list = new List<object>();
while (reader.Read())
{
switch (reader.TokenType)
{
default:
list.Add(Read(ref reader, typeof(object), options));
break;
case JsonTokenType.EndArray:
return list;
}
}
throw new JsonException();
}
case JsonTokenType.StartObject:
var dict = CreateDictionary();
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.EndObject:
return dict;
case JsonTokenType.PropertyName:
var key = reader.GetString();
reader.Read();
dict.Add(key, Read(ref reader, typeof(object), options));
break;
default:
throw new JsonException();
}
}
throw new JsonException();
default:
throw new JsonException(string.Format("Unknown token {0}", reader.TokenType));
}
}
protected virtual IDictionary<string, object> CreateDictionary() =>
ObjectFormat == ObjectFormat.Expando ? new ExpandoObject() : new Dictionary<string, object>();
}
public enum FloatFormat
{
Double,
Decimal,
}
public enum UnknownNumberFormat
{
Error,
JsonElement,
}
public enum ObjectFormat
{
Expando,
Dictionary,
}
要使用它,请反序列化为 object
(或 dynamic
,如果配置为使用 ExpandoObject
),如下所示:
var options = new JsonSerializerOptions
{
Converters = { new ObjectAsPrimitiveConverter(floatFormat : FloatFormat.Double, unknownNumberFormat : UnknownNumberFormat.Error, objectFormat : ObjectFormat.Expando) },
WriteIndented = true,
};
dynamic d = JsonSerializer.Deserialize<dynamic>(json, options);
备注:
JSON 允许任意精度和大小的数字,而 .Net 原始数字类型则不允许。在无法将某些 JSON 数字解析为 .Net 原始类型的情况下,转换器提供了选择 return 数字的
JsonElement
或抛出异常的选项。可以扩展转换器以尝试将不支持的数字反序列化为
BigInteger
。您可以将转换器配置为对浮点数使用
double
或decimal
,对 [=50= 使用Dictionary<string, object>
或ExpandoObject
] 对象。
演示 fiddle here.