【问题标题】:How to deserialize array into wrapper object?如何将数组反序列化为包装对象?
【发布时间】:2019-06-08 17:55:47
【问题描述】:

我希望 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。我想将内部数组转换为对象。

【问题讨论】:

  • 或者例如我想将根数组包装到具有数组属性的对象中。
  • 1) 您要序列化或反序列化什么 JSON?你能请edit你的问题来显示你想要的JSON吗? 2) ArrayWrapper 没有 array 的公共 getter,只有前两个条目(假定存在)。当数组长度错误时会发生什么?
  • 好的,仍然不确定我是否理解你的问题,但如果我这样做最简单的方法是 1) 使 ArrayWrapper 实现 IEnumerable<int>,以及 2) 添加 public ArrayWrapper(IEnumerable<int> enumerable) 构造函数。完成后,Json.NET 会将ArrayWrapper 视为只读集合并(反)将其序列化。演示在这里:dotnetfiddle.net/jHHx86。这就是你想要的吗?
  • 此 API 返回数组数组:github.com/binance-exchange/binance-official-api-docs/blob/… 我想将内部数组转换为对象。谢谢您的回答。我想我将不得不使用它。但这不是完美的解决方案(:
  • 但这不是完美的解决方案(: - 那我应该添加一个答案吗?或者你可以edit你的问题更清楚地解释你的要求吗?已经有关于反序列化 Binance Kline/Candlestick 数据的答案,请参阅C#: Parsing a non-JSON array-only api response to a class object with x properties

标签: c# json.net


【解决方案1】:

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

  • 为某些T(此处为int)实现IEnumerable<T>
  • 添加一个构造函数,该构造函数将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.

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

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

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

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

(您可能还想将struct 更改为class,因为可变结构是discouraged。)

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

【讨论】:

  • 我选择了第二个选项并遇到了问题。我的财产有JsonConverterArrayToObjectConverter 不使用它并抛出异常。 jToken.ToObject( prop.PropertyType, serializer ) 中的问题。 jToken 很长,但 PropertyType 是 DateTime。
  • 我想我应该把 JArray 转换成 JObject。
  • @wishmaster35 - 答案已更新,但是将来您真的应该问另一个问题用一个新的minimal reproducible example 来证明您的问题,因为最初的答案满足了您的需求原始问题。
【解决方案2】:

我制作了我的ArrayToObjectConverter 版本。

我的ArrayToObjectConverter 只需将JArray 转换为JObject 属性:Item1、Item2、Item3... 并反序列化此对象。

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 与这样的对象一起使用:

[JsonConverter( typeof( ArrayToObjectConverter ) )]
public class Obj {

    [JsonProperty]
    internal string Item0 { get; set; }
    [JsonProperty]
    internal decimal Item1 { get; set; }

    [JsonIgnore]
    public string Text { get => Item0; set => Item0 = value; }
    [JsonIgnore]
    public decimal Number { get => Item1; set => Item1 = value; }

}

只有我不确定JsonObjectContract.Properties 是否总是有正确的顺序。

【讨论】:

  • WriteJson 没有使用属性JsonConverter (:
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-19
相关资源
最近更新 更多