将非数组数据反序列化为数组
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);
}
我无法控制我的数据源。他们构造了一个 JSON 应该包含数组的字符串,但实际上没有。此外,他们为每个属性命名不同。我已经尝试了我所知道的一切,但我在尝试反序列化时不知所措。
{ "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 来处理这种反序列化,以便它忽略属性名称中的 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);
}