7

カウントに応じて、プロパティを配列またはオブジェクトとして返す外部 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 がこのケースを処理してくれることを期待しましたが、ダイスはありませんでした。

4

3 に答える 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;
}
于 2012-06-11T17:57:10.887 に答える
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 DateTime コンバーターの作成

特定の問題については、もう少し制御が必要です。次のタイプのコンバーターを試してください。

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 カスタム コンバーター – クイック ツアー

この場合、デシリアライズ時にカスタム コンバーターを渡す必要があります。

Contact contact = 
    JsonConvert.DeserializeObject<Contact>(json, new ContactConverter());
于 2012-06-11T13:26:29.677 に答える
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>);
    }
}
于 2012-07-25T22:13:58.477 に答える