2017-05-30 12 views
2

多少奇妙な形式のJSONファイルがあります。私は自分のプロジェクトのためにこのフォーマットを読み、変更して保存しなければなりません。残念ながら、私は与えられたフォーマットに影響を与えません。インスタンスのプロパティをJSONに選択的にシリアル化します

この形式の奇妙なことは、この構造体のすべてのエンティティに、(無効に)「$ id」という固有の識別子があることです。これらのインスタンスは、プロパティの完全なセットではなく、idによって参照されることもあります。この場合、 "$ id"はありませんが、代わりに既に定義されたエンティティを含む "$ ref"フィールドがあります。下記(短縮)の例を見てみましょう。これを処理するために

{ 
    "$id": "1", 
    "StyleName": "Standard", 
    "Style": { 
     "$id": "2", 
     "ShapeStyle": { 
      "$id": "3", 
      "Background": { 
       "$id": "4", 
       "Color": { 
        "$id": "5", 
        "A": 255, 
        "R": 68, 
        "G": 84, 
        "B": 106 
       } 
      } 
     }, 
     "RightEndCapsStyle": { 
      "$id": "6", 
      "Background": { 
       "$id": "7", 
       "Color": { 
        "$ref": "5" 
       } 
      } 
     } 
    } 
} 

、私はエンティティは、すべてのJSONデータクラスのためのC#の基本クラスを作成しました。この基本クラスはIDのレジストリを保持しているので、与えられた$ refのインスタンスを簡単に見つけることができます。ここでは、コードです:

public class Entity 
{ 
    public static Dictionary<string, Entity> Registry = new Dictionary<string, Entity>(); 

    private string key = string.Empty; 

    [JsonProperty("$id", Order = -2)] 
    [DefaultValue("")] 
    public string Key 
    { 
     get { return key; } 
     set 
     { 
      key = value; 
      if (!string.IsNullOrEmpty(key)) Registry.Add(key, this); 
     } 
    } 

    [JsonProperty("$ref")] 
    public string RefKey { get; set; } 

    [JsonIgnore] 
    public bool IsReference => !string.IsNullOrEmpty(RefKey); 

    [JsonIgnore] 
    public Entity RefEntity 
    { 
     get 
     { 
      Entity entity = null; 
      if (IsReference && !Registry.TryGetValue(RefKey, out entity)) 
      { 
       throw new ApplicationException("Referenced entity not found!"); 
      } 
      return entity; 
     } 
    } 
} 

JSONクラスhiearchyは次のようになります。

public class RootObject: Entity 
{ 
    public string StyleName { get; set; } 
    public StyleRoot Style { get; set; } 
} 

public class StyleRoot: Entity 
{ 
    public Style ShapeStyle { get; set; } 
    public Style RightEndCapsStyle { get; set; } 
} 

public class Style: Entity 
{ 
    public Background Background { get; set; } 
} 

public class Background: Entity 
{ 
    public Color Color { get; set; } 
} 

public class Color: Entity 
{ 
    public int A { get; set; } 
    public int R { get; set; } 
    public int G { get; set; } 
    public int B { get; set; } 
} 

をこれまでのところ、とても良いです。今、私の質問に:

  1. [JsonProperty("$id")][JsonProperty("$ref")]$ ID$ refをので、動作しません有効なJSONのフィールド名の識別子ではありません。現在、正規表現を使用して有効なものを置き換え、その後は以前のものに戻し直しています。しかし、これは遅いです。私は同じを達成するためにNamingStrategyを使用することができるかと思います。しかし、私は成功しませんでした。覚えておいて、私は両方の方法、非直列化と直列化が必要です。
  2. シリアル化はさらに困難な問題です。私の構造をシリアル化すると、各インスタンスに"$id":"$ref":というフィールドがあります。さらに、参照された項目とともに、他のすべてのフィールドはデフォルト値で直列化されます。最も簡単な方法はJsonSerializerSettingsDefaultValueHandling.IgnoreAndPopulateと設定することでした。しかし、この方法では、すべてのデフォルト値が出力から取り除かれ、予期しない動作につながります。私はContractResolverと一緒に行こうとしましたが、これは現在のプロパティに対してローカルであり、周囲のエンティティを考慮に入れることができないため失敗しました。カスタムシリアル化戦略を実装する他の方法はありますか?

私は知っています。読んで理解するためのものがたくさんあります。しかし、それが簡単なのであれば、私はここにすべてを置く努力をしなかったでしょう。あなたの助けが必死に必要です。

答えて

1

PreserveReferencesHandling modeが有効な場合、これらの$idおよび$refはJson.NETによって内部的に使用されます。あなたはそれらを気にする必要はありません、あなたはモードをJsonSerializerSettingsに設定して、通常EntityクラスなしでRootObjectStyleRootなどのクラスを定義するだけです。 Json。NETは、ボンネットの下$id sおよび$ref Sに対処し、保存元の参照構造をオブジェクト・ネットワークを作成します。

var obj = JsonConvert.DeserializeObject<RootObject>(json, 
    new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); 

シリアライズもモードハンドリング保存参考文献に行うことができる :

var json = JsonConvert.SerializeObject(obj, 
    new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }); 

デモ:https://dotnetfiddle.net/TygMDZ

NB入力データのサンプルでは、​​"$ref"属性には数値が含まれていますが、Json.NETは書き込みを行い、"$ref"の属性には文字列値が必要です。あなたが間違ってJSONテキストをいくつかの実験の過程で変更して投稿したようになるかもしれませんか?

+0

完璧!私のために働く。 あなたは "$ ref"値について正しいです。私はそれをよりコンパクトにするために多くのテキストを取り除いた。私はいくつかの手動編集をしなければならなかった... ところで、PreserveReferenceHandling.ObjectとPreserveReferenceHandling.Allの違いは何ですか? – freefall