2017-03-27 9 views
2

私は次の形式でJSONを持っています。Json.NETを使用して、参照用のカスタムフォーマットを使用するときに、参照によってオブジェクトを逆シリアル化することはできますか?

{ 
    "users": [ 
     { 
      "first_name": "John", 
      "last_name": "Smith", 
      "vet": [ "FOO", "VET-1" ], 
      "animals": [ [ "FOO", "ANIMAL-22" ] ] 
     }, 
     { 
      "first_name": "Susan", 
      "last_name": "Smith", 
      "vet": [ "FOO", "VET-1" ] 
     } 
    ], 
    "BAR": { 
     "VET-1": { 
      "vet_name": "Acme, Inc", 
      "vet_id": 456 
     }, 
     "ANIMAL-22": { 
      "animal_name": "Fido", 
      "species": "dog", 
      "animal_id": 789, 
      "vet": [ "FOO", "VET-1" ] 
     } 
    } 
} 

いくつかのネストされたオブジェクト、または複数回参照しているオブジェクトを、参照としてシリアライズされます。

参照されるオブジェクトは、JSONオブジェクトの末尾にあるBAR配列に含まれ、[ "FOO", "ANIMAL-22" ]配列で特定されます。

(いずれもFOOBARは静的定数であり、ANIMAL-22/VET-1識別子は、半ランダムである)

は残念ながら、これは一致していませんかJson.NETすでにserializes/deserializes referenced objectsと私はしていないよう実装することができIReferenceResolver行動を十分に調整することができます(it's fixed to use "$ref"開始)。

影響を受けたプロパティのカスタムJsonConverterを書きましたが、ルートオブジェクトのBARプロパティへの参照を取得できませんでした。

Json.NETをこの種のC#クラス構造に上記のJSONを逆シリアル化する方法はありますか?

public class User 
{ 
    [JsonProperty("first_name")] 
    public string FirstName { get; set; } 

    [JsonProperty("last_name")] 
    public string LastName { get; set; } 

    [JsonProperty("vet")] 
    public Vet Vet { get; set; } 

    [JsonProperty("animals")] 
    public List<Animal> Animals { get; set; } 
} 

public class Vet 
{ 
    [JsonProperty("vet_id")] 
    public int Id { get; set; } 

    [JsonProperty("vet_name")] 
    public string Name { get; set; } 
} 

public class Animal 
{ 
    [JsonProperty("animal_id")] 
    public int Id { get; set; } 

    [JsonProperty("animal_name")] 
    public string Name { get; set; } 

    [JsonProperty("vet")] 
    public Vet Vet { get; set; } 

    [JsonProperty("species")] 
    public string Species { get; set; } 
} 

編集#1:私は私の例でのみAnimalVetを与えますが、そこにこの方法で参照種類が多数あり、私は「ジェネリック」やタイプが必要だと思いますアレイ構造[ "FOO", "..." ]のこのような発生を処理する診断ソリューションは、各C#タイプを個別にコードする必要はありません。

+0

いいえ、あなたはこれを自動的に実現することができません。 Json.NETはシングルパスシリアライザですが、 '' ANIMAL-22''と '' VET-1''はJSONへの*前方参照*になりますオブジェクトは後でファイルに現れ、参照が読み込まれた時点では読み込まれません。このような参照を修正するために、デシリアライゼーション後にオブジェクトを後処理するか、LINQ to JSONを使用してJson.NETの必要な形式にJSONを前処理する必要があります。 – dbc

答えて

2

@dbcがこのコメントで述べたように、Json.Netがカスタム参照フォーマットを自動的に処理させる簡単な方法はありません。つまり、です。LINQ-to-JSON(JObjects)を使用してJSONを解析し、JsonConverterといくつかの辞書を使用して参照を解決し、クラスを作成しながらJson.Netに大量のデータを残します。ここで私はかかるだろうなアプローチです:

  1. [ "FOO", "<key>" ]参照形式をデコードして辞書から対応するオブジェクトを返すことができJsonConverter一般的なカスタムを作成します。

    public class ReferenceConverter<T> : JsonConverter 
    { 
        private Dictionary<string, T> ReferenceDict { get; set; } 
    
        public ReferenceConverter(Dictionary<string, T> referenceDict) 
        { 
         ReferenceDict = referenceDict; 
        } 
    
        public override bool CanConvert(Type objectType) 
        { 
         return objectType == typeof(T); 
        } 
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
        { 
         JArray array = JArray.Load(reader); 
         if (array.Count == 2 && 
          array[0].Type == JTokenType.String && 
          (string)array[0] == "FOO" && 
          array[1].Type == JTokenType.String) 
         { 
          string key = (string)array[1]; 
          T obj; 
          if (ReferenceDict.TryGetValue(key, out obj)) 
           return obj; 
    
          throw new JsonSerializationException("No " + typeof(T).Name + " was found with the key \"" + key + "\"."); 
         } 
    
         throw new JsonSerializationException("Reference had an invalid format: " + array.ToString()); 
        } 
    
        public override bool CanWrite 
        { 
         get { return false; } 
        } 
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
        { 
         throw new NotImplementedException(); 
        } 
    } 
    
  2. JObjectにJSONを解析し、JSONのBAR部からVetsの辞書を構築する:ここで変換するためのコードです。

    JObject data = JObject.Parse(json); 
    
    Dictionary<string, Vet> vets = data["BAR"] 
        .Children<JProperty>() 
        .Where(jp => jp.Value["vet_id"] != null) 
        .ToDictionary(jp => jp.Name, jp => jp.Value.ToObject<Vet>()); 
    
  3. Vet参照を解決するためにステップ2からReferenceConverter<T>及び辞書を用いて、JSONのBARセクションからAnimalsの辞書を構築します。

    JsonSerializer serializer = new JsonSerializer(); 
    serializer.Converters.Add(new ReferenceConverter<Vet>(vets)); 
    
    Dictionary<string, Animal> animals = data["BAR"] 
        .Children<JProperty>() 
        .Where(jp => jp.Value["animal_id"] != null) 
        .ToDictionary(jp => jp.Name, jp => jp.Value.ToObject<Animal>(serializer)); 
    
  4. 最後に、もう一度すべての参照を解決するためにReferenceConverter<T>プラス2つの辞書(今では実際には二つのコンバータインスタンス、辞書ごとに1つ)を使用して、JSONからusersリストをデシリアライズ。ここ

    serializer.Converters.Add(new ReferenceConverter<Animal>(animals)); 
    List<User> users = data["users"].ToObject<List<User>>(serializer); 
    

全デモ:https://dotnetfiddle.net/uUuy7v

+0

あなたの答えをありがとう、作業デモ!私は私の「自動的に」の使用は言葉の悪い選択だと思う。私はデシリアライズしているクラスが非常に多いので、配列構造体の[FOO "、" ... "]'のような出現を処理する "汎用的"な、あるいは無関係な解決策を期待しています各C#タイプを個別にコーディングする必要があります。私の例では、私は 'Vet'と' Animal'しか持っていませんが、実際ははるかに多くあります。また、(#2の 'vet_id 'で行ったように)プロパティを調べることによって、参照されるオブジェクトの型が決まるという保証もありません。 –

+0

私は 'BAR'辞書の内容で何かを投入するという考え方があると思います。 'JsonConverter'がプロパティーに対して呼び出され、プロパティータイプがわかるまで、参照された各_value_の逆直列化を延期する必要があります。しかし、私が知る限り、 'JsonConverter'は、JSONの配列構造体[FOO"、 "..."]の存在ではなく、逆シリアル化されている既知の型によってトリガされます。 –

関連する問題