[价格,数量]的反序列化数组



这是我试图反序列化的端点。问题在bids,askschanges内部。我该如何处理?API显示是array of [price, quantity]

片段
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public record Depth(
[property: JsonPropertyName("type")] DepthType Type,
[property: JsonPropertyName("pair")] string Pair,
[property: JsonPropertyName("sequence")] long Sequence,
[property: JsonPropertyName("asks")] List<List<string>> Asks,
//[property: JsonPropertyName("bids")] IEnumerable<Level> Bids,
[property: JsonPropertyName("prev_sequence")] long PreviousSequence
//[property: JsonPropertyName("changes")] IEnumerable<Changes> Changes
);
[JsonConverter(typeof(JsonStringEnumMemberConverter))]
public enum DepthType
{
[EnumMember(Value = "snapshot")]
Snapshot,
[EnumMember(Value = "update")]
Update
}
public record Level(decimal Price, decimal Quantity);
public record Changes(string Side, decimal Price, decimal Quantity);

反应
{
"channel":"depth",
"timestamp":1587929552250,
"module":"spot",
"data":{
"type":"snapshot",
"pair":"BTC-USDT",
"sequence":9,
"bids":[
[
"0.08000000",
"0.10000000"
]
],
"asks":[
[
"0.09000000",
"0.20000000"
]
]
}
}
{
"channel":"depth",
"timestamp":1587930311331,
"module":"spot",
"data":{
"type":"update",
"pair":"BTC-USDT",
"sequence":10,
"prev_sequence":9,
"changes":[
[
"sell",
"0.08500000",
"0.10000000"
]
]
}
}

尝试
[AttributeUsage(AttributeTargets.Property)]
public sealed class JsonPlainArrayIndexAttribute : Attribute
{
public JsonPlainArrayIndexAttribute(int index)
{
Index = index;
}
public int Index { get; }
}
public sealed class JsonPlainArrayConverter<T> : JsonConverter<T> where T : new()
{
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, 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);
}
}
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public record Depth(
[property: JsonPropertyName("type")] DepthType Type,
[property: JsonPropertyName("pair")] string Pair,
[property: JsonPropertyName("sequence")] long Sequence,
[property: JsonPropertyName("asks")] BookLevel Asks,
[property: JsonPropertyName("bids")] BookLevel Bids,
[property: JsonPropertyName("prev_sequence")] long PreviousSequence
//[property: JsonPropertyName("changes")] IEnumerable<Changes> Changes
);
[JsonConverter(typeof(JsonStringEnumMemberConverter))]
public enum DepthType
{
[EnumMember(Value = "snapshot")]
Snapshot,
[EnumMember(Value = "update")]
Update
}
[JsonConverter(typeof(JsonPlainArrayConverter<BookLevel>))]
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public record BookLevel
{
[JsonPlainArrayIndex(0)]
public string Price { get; init; }
[JsonPlainArrayIndex(1)]
public string Quantity { get; init; }
}
public record Changes(string Side, decimal Price, decimal Quantity);

从yueyinqiu的回答转换到System.Text。Json无法反序列化List>基本上按原样工作,将您的BookLevel模型绑定到与其属性对应的Json值数组。但是,您有两个问题:

  1. 由于"bids""asks"在JSON中是锯齿状的二维数组,您必须将相应的属性声明为集合,例如:

    public record Depth(
    [property: JsonPropertyName("asks")] List<BookLevel> Asks,
    [property: JsonPropertyName("bids")] List<BookLevel> Bids,
    );
    
  2. 您已将[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)[JsonConverter(typeof(JsonPlainArrayConverter<BookLevel>))]应用于BookLevel:

    [JsonConverter(typeof(JsonPlainArrayConverter<BookLevel>))]
    [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
    public record BookLevel
    {
    [JsonPlainArrayIndex(0)]
    public string Price { get; init; }
    [JsonPlainArrayIndex(1)]
    public string Quantity { get; init; }
    }
    

    结果,当您这样做时,System.Text.Json将失败,并出现一个神秘的内部System.NullReferenceException:

    System.NullReferenceException: Object reference not set to an instance of an object.
    at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_NumberHandlingOnPropertyInvalid(JsonPropertyInfo jsonPropertyInfo)
    at System.Text.Json.Serialization.Metadata.JsonPropertyInfo.DetermineNumberHandlingForTypeInfo(Nullable`1 numberHandling)
    at System.Text.Json.Serialization.Metadata.JsonPropertyInfo.GetPolicies(Nullable`1 ignoreCondition, Nullable`1 declaringTypeNumberHandling)
    at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.Initialize(Type parentClassType, Type declaredPropertyType, Type runtimePropertyType, ConverterStrategy runtimeClassType, MemberInfo memberInfo, Boolean isVirtual, JsonConverter converter, Nullable`1 ignoreCondition, Nullable`1 parentTypeNumberHandling, JsonSerializerOptions options)
    at System.Text.Json.Serialization.Metadata.JsonTypeInfo.CreateProperty(Type declaredPropertyType, Type runtimePropertyType, MemberInfo memberInfo, Type parentClassType, Boolean isVirtual, JsonConverter converter, JsonSerializerOptions options, Nullable`1 parentTypeNumberHandling, Nullable`1 ignoreCondition)
    at System.Text.Json.Serialization.Metadata.JsonTypeInfo..ctor(Type type, JsonConverter converter, Type runtimeType, JsonSerializerOptions options)
    at System.Text.Json.JsonSerializerOptions.<InitializeForReflectionSerializer>g__CreateJsonTypeInfo|112_0(Type type, JsonSerializerOptions options)
    at System.Text.Json.JsonSerializerOptions.GetClassFromContextOrCreate(Type type)
    at System.Text.Json.JsonSerializerOptions.GetOrAddClass(Type type)
    at System.Text.Json.Serialization.Metadata.JsonTypeInfo.get_ElementTypeInfo()
    at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
    at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
    at System.Text.Json.Serialization.JsonConverter`1.TryReadAsObject(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state, Object& value)
    at System.Text.Json.Serialization.Converters.LargeObjectWithParameterizedConstructorConverter`1.ReadAndCacheConstructorArgument(ReadStack& state, Utf8JsonReader& reader, JsonParameterInfo jsonParameterInfo)
    at System.Text.Json.Serialization.Converters.ObjectWithParameterizedConstructorConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
    at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
    at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
    at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo jsonTypeInfo, Nullable`1 actualByteCount)
    at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo jsonTypeInfo)
    at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options)
    

    这看起来是System.Text.Json本身的错误。如果你愿意,可以举报。即使这两个属性不打算一起工作,System.Text.Json也不应该抛出NullReferenceException.

    在这里演示一下:https://dotnetfiddle.net/sFWIJg.

作为问题#2的解决方案,您可以解除JsonPlainArrayConverter<T>的密封,以允许子类在反序列化特定属性时使用自定义选项,如下所示:

public sealed class JsonNumberHandlingPlainArrayConverter<T> : JsonPlainArrayConverter<T> where T : new()
{
protected override JsonSerializerOptions CustomizePropertyOptions(PropertyInfo info, JsonSerializerOptions options) =>
new JsonSerializerOptions(options)
{
NumberHandling = JsonNumberHandling.AllowReadingFromString,
};
}
// JsonPlainArrayIndexAttribute and  JsonPlainArrayConverter<T> adapted from this answer https://stackoverflow.com/a/71649515/3744182
// By https://stackoverflow.com/users/15283686/yueyinqiu
// To https://stackoverflow.com/questions/71649101/system-text-json-fails-to-deserialize-listlistobject
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class JsonPlainArrayIndexAttribute : Attribute
{
public int Index { get; }
public JsonPlainArrayIndexAttribute(int index) => this.Index = index;
}
public class JsonPlainArrayConverter<T> : JsonConverter<T> where T : new()
{
protected virtual JsonSerializerOptions CustomizePropertyOptions(PropertyInfo info, JsonSerializerOptions options) => 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);
}
}

然后修改BookLevel如下:

[JsonConverter(typeof(JsonNumberHandlingPlainArrayConverter<BookLevel>))]
public record BookLevel
{
[JsonPlainArrayIndex(0)]
public decimal Price { get; init; }
[JsonPlainArrayIndex(1)]
public decimal Quantity { get; init; }
}

此处演示:https://dotnetfiddle.net/AwtFni

最新更新