使用Json.net将非数组JSON对象解析为数组

7
我可以帮忙翻译。以下是您需要翻译的内容:

我正在使用一个外部API,该API返回一个属性,要么是数组,要么是对象,具体取决于数量。如何处理这种情况呢?

以数组形式返回:

{
    "contacts": {
        "address": [
            {
                "id": "47602070",
                "type": "Work",
                "street": "MyStreet",
                "city": "MyCity",
                "zip": "12345",
                "country": "USA"
            },
            {
                "id": "47732816",
                "type": "GPS",
                "street": "50.0,30.0"
            }
        ]
    }
}

以对象形式返回:

{
    "contacts": {
        "address": {
            "id": "47602070",
            "type": "Work",
            "street": "MyStreet",
            "city": "MyCity",
            "zip": "12345",
            "country": "USA"
        }
    }
}

我在考虑一个解决方法,就是使用自定义反序列化器,对于对象的情况返回长度为1的数组,对于数组的情况使用默认的反序列化,但我还不知道如何实现。

我尝试将对象反序列化成数组,并希望Json.net会为我处理这种情况,但没有成功。

3个回答

5
根据Christophe Geers的答案,这是我最终采用的方法。
  1. 创建一个自定义JSON转换器,始终将JSON解析为数组。如果JSON是非数组对象,则反序列化该对象并将其包装在一个数组中。

  2. 使用自定义转换器属性标记相应的属性。

自定义转换器

public class JsonToArrayConverter<T> : CustomCreationConverter<T[]>
{
    public override T[] Create(Type objectType)
    {
        // Default value is an empty array.
        return new T[0];
    }

    public override object ReadJson(JsonReader reader, Type objectType, object
        existingValue, JsonSerializer serializer)
    {

        if (reader.TokenType == JsonToken.StartArray)
        {
            // JSON object was an array, so just deserialize it as usual.
            object result = serializer.Deserialize(reader, objectType);
            return result;
        }
        else
        {
            // JSON object was not an arry, so deserialize the object
            // and wrap it in an array.
            var resultObject = serializer.Deserialize<T>(reader);
            return new T[] {resultObject};
        }
    }
}

问题示例的数据结构

public class Organisation
{
    public Contacts contacts;
}

public class Address
{
    public string id;
    public string street;
    public string city;
    public string type;
    public string zip;
    public string country;
}

public class Contacts
{
    // Tell JSON.net to use the custom converter for this property.
    [JsonConverter(typeof(JsonToArrayConverter<Address>))]
    public Address[] address;
}

3

一个自定义的 JSON.NET 转换器可能会解决这个问题。这并不难。

对于 DateTime 属性,你可以按照以下方式进行操作。只需使用自定义转换器装饰相关属性即可。

[JsonObject(MemberSerialization.OptIn)]
public class MyClass
{
    [JsonProperty(PropertyName = "creation_date")]
    [JsonConverter(typeof(UnixDateTimeConverter))]
    public DateTime CreationDate { get; set; }
}

JSON.NET 提供了大部分的基础设施。只需从一个基础转换器派生即可。
public class UnixDateTimeConverter : DateTimeConverterBase
{
    public override void WriteJson(JsonWriter writer, object value, 
                                   JsonSerializer serializer)
    { ...}

    public override void WriteJson(JsonWriter writer, object value, 
                                   JsonSerializer serializer)
    { ... } 
}  

您需要做的就是实现ReadJson(反序列化)和WriteJson(序列化)方法。

您可以在此处找到完整的示例:

编写自定义的 Json.NET 日期时间转换器

对于您的特定问题,您需要更多的控制。尝试以下类型的转换器:

public class Contact
{ 
   private List<Address> _addresses = new List<Address>();       
   public IEnumerable<Address> Addresses { get { return _addresses; }
}

public class ContactConverter : CustomCreationConverter<Contact>
{
    public override Contact Create(Type objectType)
    {
        return new Contact();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object 
        existingValue, JsonSerializer serializer)
    {
        var mappedObj = new Contact();

        // Parse JSON data here
        // ...

        return mappedObj;
    }
}

使用像上面那样的自定义转换器,您可以自己解析JSON数据并按照您的意愿组合Contact对象。我修改了在这里找到的一个示例:JSON.NET Custom Converters–A Quick Tour。在这种情况下,您需要在反序列化时传递自定义转换器。
Contact contact = 
    JsonConvert.DeserializeObject<Contact>(json, new ContactConverter());

感谢您的详细输入。通过您的示例,我已经让它正常工作并发布了我的最终结果。 - angularsen

1
注意:您可以使用普通的转换器而不是使用 CustomCreationConverter。例如,我使用如下代码:
public class SingleToArrayConverter<T> : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var items = (IEnumerable<T>)value;
        if (value == null || !items.Any())
        {
            writer.WriteNull();
        }
        else if (items.Count() == 1)
        {
            serializer.Serialize(writer, items.ElementAt(0));
        }
        else
        {
            serializer.Serialize(writer, items);
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (!CanConvert(objectType))
        {
            throw new NotSupportedException();
        }

        if (reader.TokenType == JsonToken.Null)
        {
            reader.Skip();
            return null;
        }
        else if (reader.TokenType == JsonToken.StartObject)
        {
            return new T[] { serializer.Deserialize<T>(reader) };
        }
        else
        {
            return serializer.Deserialize<T[]>(reader);
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(IEnumerable<T>);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接