反序列化以下json
{
"MetaData1": "hello world",
"MetaData2": 2022,
"Data": {
"ObjectA": {
"id": 1,
"name": "steve",
"hobbies": 1
},
"ObjectB": {
"id": 2,
"name": "dave",
"age": 55
}
}
}
转换为相应的c#对象
public class ObjectBase
{
public int id { get; set; }
public string name { get; set; }
}
public class ObjectA : ObjectBase
{
public int hobbies { get; set; }
}
public class ObjectB : ObjectBase
{
public int age { get; set; }
}
public class Data
{
public ObjectA ObjectA { get; set; }
public ObjectB ObjectB { get; set; }
}
public class Root
{
public string metaData1 { get; set; }
public int metaData2 { get; set; }
public Data Data { get; set; }
}
使用
Root object = JsonConvert.DeserializeObject<Root>(json);
如何在Root.Data
的对象属性的id
属性中搜索匹配的int
并返回相应的name
属性。
还可以创建List<ObjectBase>
,以便对这些对象执行其他LINQ操作。
我想我最终想要的是
List<ObjectBase>
。
这可以通过System.Text.Json
(或Newtonsoft(轻松实现。
给定Json结构,最自然的表示(IMO(是反序列化为Dictionary<string, ObjectBase>
。然后您可以将字典转换为List<ObjectBase>
。您需要一个类来匹配(更新的(Json:中的Data
元素
// 'root' class to represent the 'Data' element
public class Root
{
public string MetaData1 { get; set; }
public int MetaData2 { get; set; }
public Dictionary<string, ObjectBase> Data { get; set; }
}
// Dictionary<string, ObjectBase>
var model = JsonSerializer.Deserialize<Root>(json);
foreach (var key in model.Data.Keys)
// do something with model.Data[key].id/name
// convert to List<ObjectBase>
var list = new List<ObjectBase>(model.Data.Values);
扩展Lasse V.Karlsen的注释,您可以将所有属性添加到单个类中,并反序列化为Dictionary<string, SingleClass>
:
public class SingleClass
{
public int id { get; set; }
public string name { get; set; }
public int hobbies { get; set; }
public int age { get; set; }
// all other properties...
}
如果选择此方法,您可能需要考虑将其他属性设为null(例如,如果您有兴趣区分没有hobbies
属性或有hobbies = 0
的属性(。
上述方法将反序列化为ObjectBase
或SingleClass
。
在线演示
仅名称查找
如果您需要基于id
查找name
属性,则可以使用以下代码进行查找:
var semiParsed = JsonConvert.DeserializeObject<Dictionary<string, JObject>>(json);
var name = (from node in semiParsed.Values
let id = (int)node.GetValue("id")
where id == lookupId
select (string)node.GetValue("name"))
.FirstOrDefault();
Console.WriteLine(name);
- 首先我们将json反序列化为一个集合
- 最上面的属性名称将是
Dictionary
的密钥 - 最上面的属性对象将被视为
JObject
s(半解析的jsons(
- 最上面的属性名称将是
- 然后我们执行Linq到Json的查询
- 我们遍历
JObject
s并检索它们的id
属性GetValue
返回一个JToken
,因为我们知道它是一个数字,所以我们将它强制转换为int
- 我们基于
lookupId
执行过滤 - 我们选择
name
属性的值
- 我们遍历
- 最后,我们需要发出
FirstOrDefault
方法调用,因为上一个查询返回IEnumerable<string>
这里我假设id
是唯一的。如果提供的lookupId
没有在json中定义,那么结果将是null
。
环绕对象查找
如果您需要查找包装对象,那么还需要使用Json.NET Schema:
var generator = new JSchemaGenerator();
JSchema schemaA = generator.Generate(typeof(ObjectA));
JSchema schemaB = generator.Generate(typeof(ObjectB));
var semiParsed = JsonConvert.DeserializeObject<Dictionary<string, JObject>>(json);
var theNode = (from node in semiParsed.Values
let id = (int)node.GetValue("id")
where id == lookupId
select node)
.FirstOrDefault();
if (theNode == null)
return;
if (theNode.IsValid(schemaA))
{
var objA = theNode.ToObject<ObjectA>();
Console.WriteLine(objA.hobbies);
} else if (theNode.IsValid(schemaB))
{
var objB = theNode.ToObject<ObjectB>();
Console.WriteLine(objB.age);
}
- 首先,我们根据类定义生成两个json模式
- 然后我们执行几乎相同的查询,这里唯一的区别是
select
部分- 我们在这里返回整个
JObject
对象,而不仅仅是它的名称
- 我们在这里返回整个
- 最后执行模式验证
- 如果检索到的json与
schemaA
匹配,那么我们可以安全地将(ToObject
(转换为ObjectA
- 我们还对照
schemaB
检查json
- 如果检索到的json与
最简单的方法是将json转换为JObjects的字典,在这种情况下,您根本不需要任何类
var dict = JObject.Parse(json).Properties().ToDictionary(jo => jo.Value["id"],jo=>jo.Value);
var searchId=2;
var name = dict[searchId]["name"]; // dave
或者您可以将json反序列化为c#对象列表
List<ObjectBase> list = JObject.Parse(json).Properties()
.Select(jo => jo.Value.ToObject<ObjectBase>()).ToList();
并使用linq获取数据
正如serge在回复他的答案时指出的那样,这个使用反射的答案优化得很差。
foreach (var prop in root.GetType().GetProperties())
{
var obj = prop.GetValue(root);
if ((int) obj.GetType().GetProperty("id").GetValue(obj) == 2)
{
Console.WriteLine(obj.GetType().GetProperty("name").GetValue(obj).ToString());
break;
}
}
利用反射获取List<ObjectBase>
的另一种方法
var objects = root.Data.Objects;
List<ObjectBase> objectList = objects.GetType().GetProperties().ToList<PropertyInfo>().ConvertAll(x => (ObjectBase)x.GetValue(objects));
我将把这个答案留在这里,以防它能帮助那些无法通过去鉴定达到这一点的人(也许他们的物品没有通过去鉴定装箱(。