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

Deserializing Non-Array Data Into An Array

我无法控制我的数据源。他们构造了一个 JSON 应该包含数组的字符串,但实际上没有。此外,他们为每个属性命名不同。我已经尝试了我所知道的一切,但我在尝试反序列化时不知所措。

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

这应该是一个数组,这样我就可以将它反序列化为一个 IEnumerable 但是缺少数组括号和附加到属性名称的 underscores/numbers 真的让我失望。

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

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

我搜索了一下,看来您需要一个 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

我不知道这是否正是您要找的,但也许它会给您一些想法。

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; }
}

您可以将其反序列化为 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++;
}
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);
}