如何将数组反序列化为包装对象?

时间:2019-06-08 17:55:47

标签: c# json.net

我希望Json.NET反序列化器能够直接使用:ArrayWrapper[] Array { get; set; }属性。

我应该编写自定义JsonConverter还是更简单的方法?

public class Obj {

    //public ArrayWrapper[] Array { get; set; } // I want it to work!!!

    [JsonProperty( "array" )]
    public int[][] Array_ { get; set; }

    [JsonIgnore]
    public ArrayWrapper[] Array => Array_.Select( i => new ArrayWrapper( i ) ).ToArray();

}

public struct ArrayWrapper {

    private readonly int[] array;

    public int Item0 => array[ 0 ];
    public int Item1 => array[ 1 ];

    public ArrayWrapper(int[] array) {
        this.array = array;
    }

    public static implicit operator ArrayWrapper(int[] array) {
        return new ArrayWrapper( array );
    }
}

注意:数组数组由以下API返回:https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#klinecandlestick-data。我想将内部数组转换为对象。

2 个答案:

答案 0 :(得分:2)

如果您只是想在代理包装器对象中捕获集合,那么最简单的方法是使包装器看起来像是Json.NET的只读集合。为此,您必须:

  • IEnumerable<T>(此处为T)实施int
  • 为同一IEnumerable<T>添加一个采用T的构造函数。 (根据实验,采用T []的构造函数是不够的。)

因此,如果您按以下方式定义ArrayWrapper

public struct ArrayWrapper : IEnumerable<int>
{
    private readonly int[] array;

    public int Item0 { get { return array[ 0 ]; } }
    public int Item1 { get { return array[ 1 ]; } }

    public ArrayWrapper(int[] array) {
        this.array = array;
    }

    public ArrayWrapper(IEnumerable<int> enumerable) 
    {
        this.array = enumerable.ToArray();
    }

    public static implicit operator ArrayWrapper(int[] array) {
        return new ArrayWrapper( array );
    }

    public IEnumerator<int> GetEnumerator()
    {
        return (array ?? Enumerable.Empty<int>()).GetEnumerator();
    }

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}

您将能够将Obj序列化和反序列化为以下JSON:

{"Array":[[1,101]]}

演示小提琴#1 here

但是,在注释中您提到数组实际上具有固定的架构,如 Public Rest API for Binance: Kline/Candlestick data 中所述。如果是这样,您可以采用从this answer C#: Parsing a non-JSON array-only api response to a class object with x properties 的方法,该方法专门处理Binance Kline /烛台数据:

即对于问题中显示的特定模型,请如下修改其定义:

[JsonConverter(typeof(ObjectToArrayConverter<ArrayWrapper>))]
public struct ArrayWrapper
{
    [JsonProperty(Order = 1)]
    public int Item0 { get; set; }
    [JsonProperty(Order = 2)]
    public int Item1 { get; set; }
}

您将能够(反)序列化相同的JSON。请注意,该转换器是完全通用的,并且可以在出现将具有固定模式的数组反序列化为对象的模式时重新使用。

(由于可变结构为discouraged,因此您可能还希望将struct更改为class。)

演示小提琴#2 here和#3 here,展示了如何将JsonConverter属性应用于可序列化属性之一。

答案 1 :(得分:0)

我制作了 results = [pool.apply_async(foo, (i,)) for i in range(4)] # `pool.apply_async()` immediately returns AsyncResult (ApplyResult) object for res in results: res.get() 版。

我的error_callback只是将ArrayToObjectConverter转换为具有属性:Item1,Item2,Item3 ...的ArrayToObjectConverter,然后反序列化此对象。

JArray

您应将JObject用于这样的对象:

public class ArrayToObjectConverter : JsonConverter {

    public override bool CanRead => true;
    public override bool CanWrite => true;


    public override bool CanConvert(Type type) {
        return false;
    }

    public override object ReadJson(JsonReader reader, Type type, object existingInstance, JsonSerializer serializer) {
        var contract = (JsonObjectContract) serializer.ContractResolver.ResolveContract( type );
        var instance = existingInstance ?? contract.DefaultCreator();

        var array = JArray.Load( reader );
        var obj = Convert( array );
        serializer.Populate( obj.CreateReader(), instance );
        return instance;
    }

    public override void WriteJson(JsonWriter writer, object instance, JsonSerializer serializer) {
        var contract = (JsonObjectContract) serializer.ContractResolver.ResolveContract( instance.GetType() );
        var properties = contract.Properties.Where( IsSerializable );

        var values = properties.Select( i => i.ValueProvider.GetValue( instance ) );
        serializer.Serialize( writer, values );
    }


    // Helpers
    private static JObject Convert(JArray array) {
        var obj = new JObject();
        for (var i = 0; i < array.Count; i++) {
            obj.Add( "Item" + i, array[ i ] );
        }
        return obj;
    }

    private static bool IsSerializable(JsonProperty property) {
        return !property.Ignored && property.Readable && property.Writable;
    }


}

只有我不确定ArrayToObjectConverter是否总是正确的顺序。