2013-04-24 12 views
7

Próbuję parsować plik json za pomocą json.net. Plik wygląda tak, jak próbuję zrekrutować tę strukturę przetwarzającą wszystkie obiekty z atrybutem Title. Ale jestem zdezorientowany około JToken, JProperty, JContainer, JValue, , JValue, . Czytanie kodu źródłowego nie pozostawiło mnie znacznie mądrzejszego i żadna z próbek nie pomoże. Chcę coś na wzór:Jak wykonać rekursywne zejście jsona za pomocą json.net?

WalkNode(node, Action<Node> action) 
{ 
    foreach(var child in node.Children) 
    { 
     Action(child); 
     WalkNode(child); 
    } 
} 

Parse() 
{ 
    WalkNode(root, n=> 
    { 
     if(n["Title"] != null) 
     { 
      ... 
     } 
    }); 
} 
+1

Niektóre pytania: Powyższy przykład jest nieprawidłowy JSON. Czy obiekty zawierające właściwości X i Y powinny znajdować się w tablicy, czy też zamierzałeś, aby X i Y znajdowały się w tym samym obiekcie zawierającym? Czy w hierarchii JSON będą znajdować się tablice, które musisz przemierzyć, czy też są to tylko zagnieżdżone obiekty i właściwości? –

Odpowiedz

13

Poniższy kod powinien być bardzo zbliżony do tego, czego szukasz. Przyjąłem założenie, że istnieje zewnętrzna tablica i że tablice mogą pojawiać się w dowolnym miejscu w hierarchii. (Jeśli to nie jest prawdziwe, można uprościć kod metody WalkNode trochę, ale to powinno działać w obu kierunkach.)

using System; 
using Newtonsoft.Json; 
using Newtonsoft.Json.Linq; 

namespace JsonRecursiveDescent 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string json = 
      @"[ 
       { 
        ""X"": 
        { 
         ""Title"":""foo"", 
         ""xxxx"":""xxxx"" 
        } 
       }, 
       { 
        ""Y"": 
        { 
         ""ZZ"": 
         { 
          ""Title"":""bar"", 
          ""xxxx"":""xxxx"" 
         } 
        } 
       } 
      ]"; 

      JToken node = JToken.Parse(json); 

      WalkNode(node, n => 
      { 
       JToken token = n["Title"]; 
       if (token != null && token.Type == JTokenType.String) 
       { 
        string title = token.Value<string>(); 
        Console.WriteLine(title); 
       } 
      }); 
     } 

     static void WalkNode(JToken node, Action<JObject> action) 
     { 
      if (node.Type == JTokenType.Object) 
      { 
       action((JObject)node); 

       foreach (JProperty child in node.Children<JProperty>()) 
       { 
        WalkNode(child.Value, action); 
       } 
      } 
      else if (node.Type == JTokenType.Array) 
      { 
       foreach (JToken child in node.Children()) 
       { 
        WalkNode(child, action); 
       } 
      } 
     } 

    } 
} 
+0

Ale jeśli nie wiem, że "Tytuł" istnieje i chcę mieć wszystkie właściwości? – rodolfoprado

+2

To jest inne pytanie. Jeśli chcesz opublikować nowe pytanie opisujące to, co próbujesz zrobić, z chęcią odpowiem. Pamiętaj o oznaczeniu go jako 'json.net'. –

4

myślałem, że to moje drobnych poprawek do metody @BrianRogers WalkNode, sprawiły, że nieco bardziej wszechstronny:

private static void WalkNode(JToken node, 
           Action<JObject> objectAction = null, 
           Action<JProperty> propertyAction = null) 
{ 
    if (node.Type == JTokenType.Object) 
    { 
     if (objectAction != null) objectAction((JObject) node); 

     foreach (JProperty child in node.Children<JProperty>()) 
     { 
      if (propertyAction != null) propertyAction(child); 
      WalkNode(child.Value, objectAction, propertyAction); 
     } 
    } 
    else if (node.Type == JTokenType.Array) 
    { 
     foreach (JToken child in node.Children()) 
     { 
      WalkNode(child, objectAction, propertyAction); 
     } 
    } 
} 

Następnie OP mógłby zrobić coś takiego:

WalkNode(json, null, prop => 
{ 
    if (prop.Name == "Title" && prop.Value.Type == JTokenType.String) 
    { 
     string title = prop.Value<string>(); 
     Console.WriteLine(title); 
    } 
}); 
1

spróbować tej metody napisałem go po kilku nieudanych próbach:

private void Traverse(JToken token, TreeNode tn) 
    { 
     if (token is JProperty) 
      if (token.First is JValue) 
       tn.Nodes.Add(((JProperty)token).Name + ": " + ((JProperty)token).Value); 
      else 
       tn = tn.Nodes.Add(((JProperty)token).Name); 

     foreach (JToken token2 in token.Children()) 
      Traverse(token2, tn); 
    } 

Najpierw trzeba przekazać go kompletny plik JSON tak:

TreeNode rooty= tvu.Nodes.Add("Rooty") // not the Indian bread,just Rooty, Ok? 
JToken token = JToken.Parse(File.ReadAllText(<"Path to json file">)); 
Traverse(token, rooty); 

Done, Bom masz ten jeden: Oh nie, nie wolno mi umieścić zdjęcia. smutny.

1

Potrzebne również do zrobienia czegoś podobnego. Chciałbym zaproponować moje rozwiązanie. Ma tę zaletę:

  • nie jest rekurencyjny
  • żadnych Callbacki
  • nie przyjmując żadnej wewnętrznej struktury (tablice)
  • oddziela przechodzenie drzewa z działania potrzebne do wykonania

    IEnumerable<JToken> AllTokens(JObject obj) { 
        var toSearch = new Stack<JToken>(obj.Children()); 
        while (toSearch.Count > 0) { 
         var inspected = toSearch.Pop(); 
         yield return inspected; 
         foreach (var child in inspected) { 
          toSearch.Push(child); 
         } 
        } 
    } 
    

    Następnie możesz użyć linq do filtrowania i wykonania akcji:

    var tokens = AllTokens(jsonObj); 
    var titles = tokens.Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == "Title"); 
    
Powiązane problemy