首页 > 解决方案 > Newtonsoft JConverter读取对象或数组 (oneOf)

问题描述

我需要反序列化 JSON,其中发送者可以发送单个实例Child作为节点之一,或者Child[]. (又名 JsonSchema 'oneOf`)。

public class Parent
{
  public Child[] Data { get; set; }
}

public class Child
{
  public string ChildName { get; set; }
}

JSON可以是数组

{
  "data":
    [
      {
        "childName": "Bob"
      }
    ]
}

或单个对象

{
  "data":
    {
      "childName": "Bob"
    }
}

这是我到目前为止所拥有的

public class ObjectToArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T[]);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            JObject singleJObject = (JObject)serializer.Deserialize(reader);
            return new T[] { singleJObject.Value<T>() };
        }
        var arrayJObject = (JObject)serializer.Deserialize(reader);
        return arrayJObject.Value<T[]>();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

我这样使用

var settings = new JsonSerializerSettings();
settings.Converters.Add(new ObjectToArrayConverter<Child>());
var parent = JsonConvert.DeserializeObject<Parent>(json, settings);

但是当 JSONdata节点是单个对象时,该行return new T[] { jObject.Value<T>() };给了我异常

System.InvalidCastException:'无法将 Newtonsoft.Json.Linq.JObject 转换为 Newtonsoft.Json.Linq.JToken。'

当 JSONdata节点是一个对象数组时,该行var arrayJObject = (JObject)serializer.Deserialize(reader);给了我异常

System.InvalidCastException:'无法将'Newtonsoft.Json.Linq.JArray'类型的对象转换为'Newtonsoft.Json.Linq.JObject'类型。

标签: c#json.net

解决方案


这里的问题是您使用的是扩展方法Value而不是ToObject. 值转换数据的“值”部分,因此在您的情况下为“鲍勃”。ToObject 将整个对象转换{"childName":"Bob"}为提供的类。

    return arrayJObject.ToObject<T[]>(); //This should work and...
    return new T[] { singleJObject.ToObject<T>() };

另外,我建议改成这个 - 所以它会更具可读性

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var result = (JToken)serializer.Deserialize(reader);
    if (result == null)
    {
        throw new ArgumentNullException(nameof(result));
    }

    if (result.Type == JTokenType.Object)
    {
        return new T[] { result.ToObject<T>() };
    }
    else if(result.Type == JTokenType.Array)
    {
        return result.ToObject<T[]>();
    }
    throw new NotSupportedException();
}

推荐阅读