用System.Text.Json反序列化数组



可以使用Json.NET反序列化数组。如何使用System.Text.Json反序列化以下JSON ?

JSON
[340,{"a":["21040.00000",0,"0.00500000"],"b":["21037.70000",0,"0.49900000"],"c":["21039.00000","0.06009660"],"v":["660.49276224","3641.23932460"],"p":["20783.06665","20853.16080"],"t":[6207,22883],"l":["20500.10000","20500.10000"],"h":["21052.40000","21528.70000"],"o":["20716.30000","21416.30000"]},"ticker","XBT/USD"]

Json。净方式

[JsonConverter(typeof(ArrayConverter))]
public class KrakenSocketEvent<T>
{
[ArrayProperty(0)]
public int ChannelId { get; set; }
[ArrayProperty(1)]
[JsonConversion]
public T Data { get; set; } = default!;
[ArrayProperty(2)]
public string Topic { get; set; } = string.Empty;
[ArrayProperty(3)]
public string Symbol { get; set; } = string.Empty;
}

编辑

我目前拥有的是以下内容。问题在于类型参数。我得到一个编译时错误。

[JsonConverter(typeof(JsonNumberHandlingPlainArrayConverter<KrakenSocketEvent<T>>))] // Attribute argument cannot use type parameters
public record KrakenSocketEvent<T> where T : new()
{
[JsonPlainArrayIndex(0)]
public int ChannelId { get; init; }
[JsonPlainArrayIndex(1)]
public T Data { get; init; } = default!;
[JsonPlainArrayIndex(2)]
public string Topic { get; init; } = null!;
[JsonPlainArrayIndex(3)]
public string Symbol { get; init; } = null!;
}
public sealed class JsonNumberHandlingPlainArrayConverter<T> : JsonPlainArrayConverter<T> where T : new()
{
protected override JsonSerializerOptions CustomizePropertyOptions(PropertyInfo info, JsonSerializerOptions options)
{
return new JsonSerializerOptions(options)
{
NumberHandling = JsonNumberHandling.AllowReadingFromString
};
}
}
[AttributeUsage(AttributeTargets.Property)]
public sealed class JsonPlainArrayIndexAttribute : Attribute
{
public JsonPlainArrayIndexAttribute(int index)
{
Index = index;
}
public int Index { get; }
}
public class JsonPlainArrayConverter<T> : JsonConverter<T> where T : new()
{
protected virtual JsonSerializerOptions CustomizePropertyOptions(PropertyInfo info, JsonSerializerOptions options)
{
return options;
}
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeof(T) == typeToConvert);
var props = typeToConvert.GetProperties();
var linq = from prop in props
let attr = prop.GetCustomAttributes(typeof(JsonPlainArrayIndexAttribute), true)
where prop.CanWrite && attr.Length is 1
orderby ((JsonPlainArrayIndexAttribute)attr[0]).Index
select prop;
var arr = JsonSerializer.Deserialize<IEnumerable<JsonElement>>(ref reader, options);
if (arr is null)
{
return default;
}
var result = new T();
foreach (var (prop, value) in linq.Zip(arr))
{
prop.SetValue(result, value.Deserialize(prop.PropertyType, CustomizePropertyOptions(prop, options)));
}
return result;
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
var type = typeof(T);
var props = type.GetProperties();
var linq = from prop in props
let attr = prop.GetCustomAttributes(typeof(JsonPlainArrayIndexAttribute), true)
where prop.CanRead && attr.Length is 1
orderby ((JsonPlainArrayIndexAttribute)attr[0]).Index
select prop.GetValue(value);
JsonSerializer.Serialize<IEnumerable<object>>(writer, linq, options);
}
}

您不能在转换器声明中使用通用T,它是一个未知值。对于这个场景,您必须创建一个JsonConverterFactory,这样您就可以确定T的类型,然后实例化您的实际转换器。

如果你有一个接口来检测或者一些属性来标记的话,写起来就容易多了。然后,您可以编写工厂来预期这一点。然后在您的转换器中,让序列化器处理如何处理序列化,只需指导它如何读/写值。

[JsonPlainArray(4)]
public record KrakenSocketEvent<T> where T : new()
{
[JsonPlainArrayIndex(0)]
public int ChannelId { get; init; }
[JsonPlainArrayIndex(1)]
public T Data { get; init; } = default!;
[JsonPlainArrayIndex(2)]
public string Topic { get; init; } = null!;
[JsonPlainArrayIndex(3)]
public string Symbol { get; init; } = null!;
}
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct)]
public sealed class JsonPlainArrayAttribute : Attribute
{
public JsonPlainArrayAttribute(int length) => Length = length;
public int Length { get; }
}
[AttributeUsage(AttributeTargets.Property)]
public sealed class JsonPlainArrayIndexAttribute : Attribute
{
public JsonPlainArrayIndexAttribute(int index) => Index = index;
public int Index { get; }
}
public class JsonPlainArrayConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert.GetConstructor(Type.EmptyTypes) != null // has default ctor
&& typeToConvert.GetCustomAttribute<JsonPlainArrayAttribute>() != null;
}
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
var converterType = typeof(_JsonPlainArrayConverter<>).MakeGenericType(typeToConvert)!;
var length = typeToConvert.GetCustomAttribute<JsonPlainArrayAttribute>()?.Length;
var properties = typeToConvert.GetProperties()
.Select((p, i) => (i, prop: p, attr: p.GetCustomAttribute<JsonPlainArrayIndexAttribute>()))
.Where(x => x.i == x.attr?.Index)
.OrderBy(x => x.i)
.Select(x => x.prop)
.ToArray();
if (length is null || properties.Length != length)
throw new Exception("bad configuration");
return Activator.CreateInstance(converterType, (object)properties) as JsonConverter;
}
private class _JsonPlainArrayConverter<T> : JsonConverter<T>
where T: new()
{
private readonly PropertyInfo[] properties;
public _JsonPlainArrayConverter(PropertyInfo[] properties) => this.properties = properties;

public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var arr = JsonSerializer.Deserialize<JsonElement[]>(ref reader, options);
if (arr is null)
return default;
var result = new T();
foreach (var x in properties.Zip(arr))
{
var value = x.Second.Deserialize(x.First.PropertyType, options);
x.First.SetValue(result, value);
}
return result;
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
if (value is null)
{
writer.WriteNullValue();
return;
}
writer.WriteStartArray();
foreach (var prop in properties)
{
var val = prop.GetValue(value);
JsonSerializer.Serialize(writer, val, prop.PropertyType, options);
}
writer.WriteEndArray();
}
}
}

用法:

var options = new JsonSerializerOptions
{
Converters =
{
new JsonPlainArrayConverter(),
},
};
var obj = JsonSerializer.Deserialize<KrakenSocketEvent<YourType>>(json, options);

最新更新