将非数组数据反序列化为数组

时间:2015-01-03 03:04:45

标签: json json.net

我无法控制我的数据源。他们构造了一个应该包含数组的JSON字符串,但它没有。此外,他们以不同的方式命名每个属性。我已经尝试了所有我知道的东西,但我不知道是不是想把它反序列化。

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

这应该是一个数组,因此我可以将其反序列化为IEnumerable< property>但缺少数组括号和附加到属性名称的下划线/数字真的让我失望。

我已经能够将字符串反序列化为单独的编号属性,然后将它们添加到集合中,但这不会起作用,因为属性的数量可能会变得很长(大约200或更多)并且我不会事先不知道JSON字符串中将包含多少属性。

有没有办法使用Newtonsoft-Json来处理这种反序列化,以便忽略属性名中的下划线/数字并将其作为数组属性名称(别名)处理?我可以控制构建代表JSON字符串的模型,但我不确定它是如何帮助的。

4 个答案:

答案 0 :(得分: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, paul@pablissimo.com, 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

答案 1 :(得分: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; }
}

答案 2 :(得分: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++;
}

答案 3 :(得分: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);
}