2016-03-25 5 views
2

У меня есть древовидная структура, представляющая сотрудников в организационной структуре.Generic Tree to JSON

Дерево состоит из графика пользовательских Node<Person> объектов, которые имеют ссылки друг на друг и другие свойства, таких как уровень, показывающие их уровень в дереве, родитель, sibilings и т.д.

Я должен сериализовать часть этого организационную диаграмму от конкретного Лица, проходящего через всех ниже них, и у меня есть метод в объекте Node, который называется SelfAndDescendants(), который возвращает IEnumerable<Node<Person>>.

Итак, в основном я определяю Node конкретного человека в дереве, а затем получаю их и всех их потомков в IEnumerable. Эта часть работает нормально.

Вот где я застрял. Теперь мне нужно получить этот IEnumerable набор Nodes в иерархический 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)); 
      } 
     } 

} 

Человек класс просто класс 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 чрезвычайно прост в этом отношении, это просто имя и название лица. Это может быть даже одна строка для каждого сотрудника.

+0

Является 'Node' сериализации? Если это так, похоже, что исходный объект «Node» имел бы структуру, которую вы хотите. 'SelfAndDescendants' звучит так, будто он выравнивает структуру. Было бы полезно, если бы вы предоставили код для «Node» и «Person», а также желаемую структуру JSON для других. –

+0

@Layoric Я обновил свой вопрос, чтобы включить дополнительную информацию о классах Node и Person. Любая помощь будет оценена по достоинству. Я не знаю, как обращаться с этим сложным классом Node, когда JSON просто нуждается в связке имен людей в иерархической структуре. Я подумал о создании гораздо более простого класса, а затем переместил все данные, чтобы он мог сериализоваться, но я не уверен. – Patrick

+0

У вас есть контроль над классом 'Node'? –

ответ

2

Да, учитывая ваши ограничения, я что создание пользовательского JsonConverter является подходящим решением для этой ситуации. На самом деле довольно просто написать, поскольку Node<T> имеет общедоступные свойства, позволяющие по крайней мере читать доступ к Value и Children. Вам не нужно беспокоиться о цикле; сериализатор перезвонит в конвертер для каждого дочернего элемента, когда он выполняет итерацию по коллекции Children по вызову JArray.FromObject.

Вот как я бы писать:

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-файл не имеет никакой связанной с ним информации о стиле.» ... Я использую return 'Request.CreateResponse (HttpStatusCode.OK, json)' .. тип возвращаемого метода - 'HttpResponseMessage', и когда я смотрю в переменной json в отладчике с визуализатором JSON выглядит корректно. Fiddler также способен анализировать ответ на действительное дерево JSON. Но он начинается в браузере (Chrome) с помощью '' – Patrick

+1

О, вы используете веб-API? В этом случае, создав ответ, убедитесь, что вы помещаете JSON в объект StringContent следующим образом: 'HttpResponseMessage response = Request.CreateResponse (HttpStatusCode.OK); response.Content = new StringContent (json, Encoding.UTF8, "application/json"); 'Это будет препятствовать веб-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