2014-12-30 2 views
4

Предположим, у меня есть следующие JToken:уплощение в JToken

@"{ 
    ""data"": [ 
     { 
      ""company"": { 
       ""ID"": ""12345"", 
       ""location"": ""Some Location"" 
      }, 
      ""name"": ""Some Name"" 
     } 
    ] 
}"; 

Я хочу передать этот маркер в функцию FlattenToken, которая выводит эту JToken:

@"{ 
    ""data"": [ 
     { 
      ""company_ID"": ""12345"", 
      ""company_location"": ""Some Location"", 
      ""name"": ""Some Name"" 
     } 
]}" 

Причина делать это так что я могу взять сплющенный JToken и десериализовать его в DataTable.

Я теряюсь в беспорядке JObjects, JTokens, JProperties и других JMadness. Я видел ответ на this post, который был полезен, но я все еще не понимаю.

Вот что я до сих пор:

public static JToken FlattenToken(JToken token) 
{ 
    foreach (JToken topLevelItem in token["data"].Children()) 
    { 
     foreach (JToken field in topLevelItem.Value<JToken>()) 
     { 
      foreach (JProperty property in field.Value<JObject>().Properties()) 
      { 
       field.AddAfterSelf(JObject.Parse(@"{""" + property.Name + "_" + property.Value)); 
      } 

      field.Remove(); 
     } 
    } 

    return token; 
} 

Первая итерация внешнего цикла Еогеасп, topLevelItem =

{ 
    "company": { 
     "ID": "12345" 
    }, 
    "name": "Some Name" 
} 

И первой итерации через второй цикл по каждому элементу, поле =

"company": { 
    "ID": "12345" 
} 

Хорошо выглядит до сих пор. Но когда я попадаю в самый внутренний цикл foreach, я получаю исключение в строке foreach: «Нельзя использовать Newtonsoft.Json.Linq.JProperty для Newtonsoft.Json.Linq.JToken».

Не уверен, что там происходит. У меня создалось впечатление, что вызов field.Value собирался создать JToken и попытаться передать его в JProperty. Итак, где же JProperty пытается попасть в JToken, как показывает ошибка?

Кроме того, это похоже на довольно грубый способ сглаживания JToken. Есть ли способ лучше?

ответ

5

Иерархия объектов в Json.NET может быть довольно глубокой. Грубое руководство можно найти в this answer.

Чтобы решить проблему, необходимо сначала метод расширения принять свойства JObject и вернуться затем в коллекции с именем префиксом:

public static class JsonExtensions 
{ 
    public static IEnumerable<KeyValuePair<string, JToken>> FlattenFields(this JObject obj, string prefix) 
    { 
     foreach (var field in obj) 
     { 
      string fieldName = prefix + "_" + field.Key; 
      var fieldValue = field.Value; 
      yield return new KeyValuePair<string, JToken>(fieldName, fieldValue); 
     } 
    } 
} 

Далее, вам понадобятся некоторые рекурсивные инструменты для перебора иерархия Json.NET и переписать коллекцию свойств выбранных JObject-х:

public static class JsonExtensions 
{ 
    public static IEnumerable<T> Yield<T>(this T item) 
    { 
     yield return item; 
    } 

    public static JToken EditFields(this JToken token, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor) 
    { 
     if (token == null) 
      return null; 
     switch (token.Type) 
     { 
      case JTokenType.Array: 
       return EditFields((JArray)token, editor); 
      case JTokenType.Object: 
       return EditFields((JObject)token, editor); 
      default: 
       return token; 
     } 
    } 

    static JToken EditFields(JArray array, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor) 
    { 
     JArray newArray = null; 
     foreach (var element in array) 
     { 
      var newElement = EditFields(element, editor); 
      if (newElement != null) 
      { 
       if (newArray == null) 
        newArray = new JArray(); 
       newArray.Add(newElement); 
      } 
     } 
     return newArray; 
    } 

    static JToken EditFields(JObject obj, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor) 
    { 
     JObject newObj = null; 

     foreach (var field in obj) 
     { 
      foreach (var newField in editor(field)) 
      { 
       if (newObj == null) 
        newObj = new JObject(); 
       newObj[newField.Key] = newField.Value.EditFields(editor); 
      } 
     } 

     return newObj; 
    } 
} 

Наконец, положить их вместе, чтобы создать метод, который способствует свойства имени JObject Prope RTY их родителей JObject, предваряя имя свойства плюс подчеркивание:

public static class JsonExtensions 
{ 
    public static JToken PromoteNamedPropertiesToParents(this JToken token, string propertyName) 
    { 
     return token.EditFields(pair => 
     { 
      if (pair.Key == propertyName && pair.Value is JObject) 
      { 
       return ((JObject)pair.Value).FlattenFields(pair.Key); 
      } 
      return pair.Yield(); 
     }); 
    } 
} 

И затем, чтобы тест:

public static class TestFlatten 
{ 
    public static void Test() 
    { 
     string jsonString = @"{ 
      ""data"": [ 
       { 
        ""company"": { 
         ""ID"": ""12345"", 
         ""location"": ""Some Location"" 
        }, 
        ""name"": ""Some Name"" 
       } 
      ] 
     }"; 
     JObject obj = JObject.Parse(jsonString); 
     var newObj = (JObject)obj.PromoteNamedPropertiesToParents("company"); 
     Debug.WriteLine(newObj); 
    } 
} 

И выход:

{ 
    "data": [ 
    { 
     "company_ID": "12345", 
     "company_location": "Some Location", 
     "name": "Some Name" 
    } 
    ] 
} 

Что, что вы хотеть. Обратите внимание, что этот код создает новую иерархию JObject вместо изменения исходной иерархии.

+1

Это фантастика. Очень полезно! – jrsowles

Смежные вопросы