2015-01-03 3 views
0

У меня нет контроля над моим источником данных. Они построили строку JSON, которая должна содержать массивы, но это не так. Кроме того, они назвали каждое свойство по-разному. Я пробовал все, что знаю, но я в затруднении, пытаясь десериализировать это.Deserializing Non-Array Data Into Array

{ "class-A" : { "property_0" : { "item1" : "data", "item2" : "data" }, "property_1" : { "item1" : "data", "item2" : "data" }, "property_2" : { "item1" : "data", "item2" : "data" } } } 

Это должно быть массивом, так что я могу десериализацию его в IEnumerable < собственности > но отсутствие скобок массива и подчеркивание/номер добавляемого к имени свойства действительно бросал меня.

Я смог десериализовать строку в отдельные пронумерованные свойства, а затем добавить их в коллекцию, но это не сработает, так как количество свойств может быть длинным (порядка 200 или более) и я не знаю заранее, сколько свойств будет включено в строку JSON.

Есть ли способ справиться с этой десериализацией с помощью Newtonsoft-Json, чтобы он игнорировал символ подчеркивания/числа в имени свойства и вместо этого обрабатывал его как имя свойства массива (псевдоним)? У меня есть контроль над построением модели, которая представляет строку JSON, но я не уверен, как это помогает.

+0

Вы должны запросить у поставщика JSON для ваших требований, чтобы сделать вашу жизнь проще – agentpx

+0

она действует на JSONLint.com по путь – agentpx

+0

Даже если вы не можете изменить свой источник данных, я думаю, что это поможет людям ответить на вопрос, знают ли они, как вы хотите, чтобы ваши данные выглядели. – morningstar

ответ

0
JObject response = JObject.Parse(jsonString); 
JEnumerable<JToken> jTokens = response["class-A"].Children(); 

IList<Property> properties = new List<Property>(); 

foreach (var jToken in jTokens) 
{ 
    JsonProp jsonProp = jToken.First.ToObject<JsonProp>(); 

    Property property = new Property(); 

    property.Item1 = jsonProp.item1; 
    property.Item2 = jsonProp.item2; 

    properties.Add(property); 
} 
0

Я искал и выглядит так, как будто вам нужен JSONConverter.

/// <summary> 
/// A JsonConverter that respects the Name property of DataMember attributes 
/// applied to enumeration members, falling back to the enumeration member 
/// name where no DataMember attribute exists (or where a name has not 
/// been supplied). Entirely experimental, use at your own risk. 
/// 
/// Paul O'Neill, [email protected], 31/07/13 
/// </summary> 
public class DataMemberAwareEnumJsonConverter : JsonConverter 
{ 
    private static Dictionary<Type, IEnumerable<Tuple<object, string>>> _typeNameCache = 
     new Dictionary<Type, IEnumerable<Tuple<object, string>>>(); 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType.IsEnum; 
    } 

    public override object ReadJson(JsonReader reader, Type type, object existingValue, JsonSerializer serializer) 
    { 
     return GetOutputValue(reader.Value.ToString(), type); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     serializer.Serialize(writer, GetOutputName(value)); 
    } 

    private static string GetOutputName(object value) 
    { 
     Type type = value.GetType(); 
     if (!type.IsEnum) 
     { 
      throw new InvalidOperationException("Type is not an enumeration"); 
     } 

     var map = GetOutputMap(type); 
     var match = map.FirstOrDefault(x => x.Item1.Equals(value)); 
     if (match != null) 
     { 
      return match.Item2; 
     } 
     else 
     { 
      // We're buggered if this is a flags enum so just return the string representation 
      return value.ToString(); 
     } 
    } 

    private static object GetOutputValue(string serialised, Type type) 
    {    
     if (!type.IsEnum) 
     { 
      throw new InvalidOperationException("Type is not an enumeration"); 
     } 

     var map = GetOutputMap(type); 
     var match = map.FirstOrDefault(x => x.Item2.Equals(serialised)); 
     if (match != null) 
     { 
      // Immediate hit, just use it 
      return match.Item1; 
     } 
     else 
     { 
      // No hit, which suggests a straight Enum.Parse should work 
      // (or fail because we've been supplied nonsense) 
      return Enum.Parse(type, serialised); 
     } 
    } 

    private static IEnumerable<Tuple<object, string>> GetOutputMap(Type type) 
    { 
     IEnumerable<Tuple<object, string>> enumOutputLookup = null; 
     if (!_typeNameCache.TryGetValue(type, out enumOutputLookup)) 
     { 
      // Index the type naively - it's unlikely we'll have more than a handful of 
      // enum values per type 
      List<Tuple<object, string>> outputNames = new List<Tuple<object, string>>(); 
      foreach (var field in type.GetFields(BindingFlags.Static | BindingFlags.Public)) 
      { 
       var dataMemberAttribute = Attribute.GetCustomAttribute(field, typeof(DataMemberAttribute)) as DataMemberAttribute; 
       if (dataMemberAttribute != null && !string.IsNullOrWhiteSpace(dataMemberAttribute.Name)) 
       { 
        outputNames.Add(new Tuple<object, string>(field.GetValue(null), dataMemberAttribute.Name)); 
       } 
       else 
       { 
        // No attribute, so go with the string representation of the field 
        outputNames.Add(new Tuple<object, string>(field.GetValue(null), field.Name)); 
       } 
      } 

      enumOutputLookup = outputNames; 
      _typeNameCache[type] = outputNames; 
     } 

     return enumOutputLookup; 
    } 
} 

От http://pablissimo.com/572/getting-newtonsoft-json-net-to-respect-the-datamember-name-property

и

https://gist.github.com/Pablissimo/6123242#file-datamemberawareenumjsonconverter

+0

О, ничего себе! Я собираюсь дать ему попробовать сейчас и посмотреть, что произойдет. –

+0

Ну, это не сработает, потому что вам все равно нужно украсить каждое свойство новым именем, но я мог бы что-то сделать в пользовательском конвертере (возможно, используя String.StartsWith), чтобы помочь. –

+0

Хорошо, не проблема Я удалю этот ответ сейчас. – agentpx

0

Я не знаю, если это именно то, что вы ищете, но, возможно, это даст вам некоторые идеи.

var jObject = JObject.Parse(json); 
var jsonPathResult = jObject.SelectTokens("$.class-A.*"); 
var collection = new List<Items>(); 
jsonPathResult.ToList().ForEach(m => collection.Add(new Items { item1 = m.Values().ElementAt(0).ToString(), item2 = m.Values().ElementAt(1).ToString() })); 

Items Где находится

public class Items 
{ 
    public string item1 { get; set; } 
    public string item2 { get; set; } 
} 
0

Вы можете десериализации его в качестве словаря C#, так что разные имена свойств в конечном итоге в качестве ключей.

class Items 
{ 
    string item1; 
    string item2; 
} 

Dictionary<string, Dictionary<string, Items>> data = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, Items>>(json); 
Dictionary<string, Items> classA = data["class-A"]; 

Затем цикл по всем показателям и построить массив:

List<Items> items = new List<Items>(); 
int i = 0; 
while (classA.ContainsKey("property_" + i.ToString())) 
{ 
    items.Add(classA["property_" + i.ToString()]); 
    i++; 
} 
+0

Я придумал вышеупомянутое решение, прежде чем увидеть ваше решение. Мне нужно будет поэкспериментировать с вашим и посмотреть, какая из них лучше, если таковая имеется. –