2016-03-25 9 views
2

私は、組織図の従業員を表す一般的なツリー構造を持っています。ジェネリックツリーからJSON

ツリーは、ツリー内の自分のレベルを示すレベル、親、sibilingsなどお互いおよび他の特性への参照を持っているカスタムNode<Person>オブジェクトのグラフから成り、等

私はこの部分をシリアル化する必要が特定のPersonからその下の全員を対象とした組織図を作成し、ノードオブジェクトにSelfAndDescendants()というメソッドを持ち、IEnumerable<Node<Person>>を返します。

基本的に私は特定の人物のNodeをツリーに配置し、それらの子孫すべてをIEnumerableに入れます。この部分はうまく動作します。

それは私が立ち往生している場所です。今私はこのIEnumerableNodesのセットを階層JSONにする必要があります。

私の最初の試みは、JSONシリアライザで直接スローするだけでしたが、これは一般的なNodeオブジェクトのセットであるため動作しませんでした(実際には期待しませんでした)。 NodeオブジェクトにValueプロパティがあり、Personオブジェクトを返します...これはJSON(名前のみ)に入るために必要なものです。

string json = JsonConvert.SerializeObject(personNode.SelfAndDescendants.ToList()); 

これは明らかに私は必要なものではありません。この時点でList<Node<Person>>をシリアル化しようとしています。すべてのJSONリターンのニーズは、単純にPersonオブジェクトのNameという階層形式です。他に何もない。

カスタムJSONをビルドして返すために、ここでループで何か手動で行う必要がありますか?

これはthis postの複製ではありません。私はここでは一般的な再帰的なツリーを扱っているので、単純な汎用データ構造ではありません。

カスタムJsonConverterを実装する必要がありますか?これは一連のNodeオブジェクトではどのように動作しますか?

Nodeクラスは、プロパティのすべての種類を持っているが、それは基本的に次のようになります。

public class Node<T> : IEqualityComparer, IEnumerable<T>, IEnumerable<Node<T>> { 

     public Node(T value) { 
      Value = value; 
     } 

     public Node<T> this[int index] { 
      get { 
       return _children[index]; 
      } 
     } 

     public Node<T> Add(T value, int index = -1) { 
      var childNode = new Node<T>(value); 
      Add(childNode, index); 
      return childNode; 
     } 

     public IEnumerable<Node<T>> SelfAndDescendants { 
      get { 
       return this.ToIEnumarable().Concat(Children.SelectMany(c => c.SelfAndDescendants)); 
      } 
     } 

} 

Personクラスは人を表すだけでPOCOクラスです。このクラスは、すでにシステムの別の部分でJSONに正しくシリアル化されています。

[JsonObject] 
    public class Person { 

     public string Title { get; set; } 

     public DateTime DateOfBirth { get; set; } 

     [JsonConverter(typeof(StringEnumConverter))] 
     public Gender Gender { get; set; } 
     public List<StreetAddress> Addresses { get; set; } 

     ... etc 

} 

希望する出力は、組織図で、JSONのユーザーのレベルを示しています。従業員の上司、上司の上司など。

JSONはこの点で非常に単純です。それは人名とタイトルです。従業員1人当たりの文字列でも構いません。

+0

は 'Node'直列化可能ですか?もしそうなら、元の 'Node'オブジェクトがあなたが望む構造を持つように思えます。 'SelfAndDescendants'は構造を平坦化するように聞こえます。 'Node'と' Person'のコードを提供するだけでなく、他の人が利用できるように目的のJSON構造体を提供すると役に立ちます。 –

+0

@Layoric私は質問を更新して、NodeクラスとPersonクラスに関するさらに詳しい情報を追加しました。どんな助けもありがとう。 JSONが階層構造内にたくさんの人の名前を必要とするだけで、この複雑なNodeクラスをどのように扱うのか分かりません。はるかに単純なクラスを作成し、すべてのデータを移動してシリアル化できると考えましたが、わかりません。 – Patrick

+0

あなたは 'Node'クラスを制御できますか? –

答えて

2

をシリアライズを追加することにより、シリアル化する必要があるかを説明したいですそのような場合には、JsonConverterというカスタムを作成することが適切な解決策であると言えます。 Node<T>が少なくともValueChildrenへの読み取りアクセスを許可する公開プロパティを持っている限り、実際に書くのはかなり簡単です。ループについて心配する必要はありません。シリアライザはJArray.FromObjectコールを介してChildrenコレクションを反復するごとに各子のコンバータにコールバックします。

は、ここで私はそれを書くだろう方法は次のとおりです。

public class OrgChartConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return (objectType == typeof(Node<Person>)); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     Node<Person> node = (Node<Person>)value; 
     JObject obj = new JObject(); 
     obj.Add("Name", node.Value.Name); 
     obj.Add("Subordinates", JArray.FromObject(node.Children, serializer)); 
     obj.WriteTo(writer); 
    } 

    public override bool CanRead 
    { 
     get { return false; } 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

次に、このようにコンバータを使用します。ここでは

var settings = new JsonSerializerSettings 
{ 
    Converters = new List<JsonConverter> { new OrgChartConverter() }, 
    Formatting = Formatting.Indented 
}; 

string json = JsonConvert.SerializeObject(rootNode, settings); 

はデモです:https://dotnetfiddle.net/BfdFdW

+0

素晴らしい。これは私が必要としたものであり、コードサンプルに感謝します。私はすぐにそれらを適切な場所に置くことができ、JSONは完全ではありませんが、私は細部の細部を処理することができます。 – Patrick

+0

何らかの理由でこれがXMLとして返されています...それがどういう考えですか? "このXMLファイルにはスタイル情報は含まれていません。" ...私は 'Request.CreateResponse(HttpStatusCode.OK、json)'を返します。メソッドの戻り値の型は 'HttpResponseMessage'です。デバッガのjson変数でJSONビジュアライザが正しく表示されます。 Fiddlerは応答を有効なJSONツリーに解析することもできます。しかし、それはブラウザで(クロム) '' – Patrick

+1

で始まります。あなたはWeb APIを使用していますか?その場合、レスポンスを作成するときは、JSONを次のように 'StringContent'オブジェクトに入れてください:' HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK); (JSON、Encoding.UTF8、 "application/json"); 'これは、Web APIが(XMLまたはJSONのいずれかに)それを二重化しようとしないようにします。 –

1

あなたは、私が、このクラスはシリアライズクラスの属性とメンバーに必要な、その後の文字列にはい、あなたの制約が与えられた

string nodes = JsonConvert.SerializeObject<Node<Person>>(personNode); 

[JsonObject] 
public class Node<T> : IEqualityComparer, IEnumerable<T>, IEnumerable<Node<T>> { 

    [JsonProperty] 
    public IEnumerable<Node<T>> Children { get { return _children; } } 

    ... 

} 

JSON.NET Serialization Attributes