让我向您展示两种不同的方式(一种简单的实现方式和一种更高级的方式)
开始之前
为了能够测试解决方案,我对您的类进行了一些小改动
public class CPDocs
{
public string DocumentDetails { get; set; }
public bool IsApproved { get; set; }
}
public class Person
{
public string Name { get; set; }
public string Age { get; set; }
public string Car { get; set; }
public bool IsApproved { get; set; }
}
-
CPDocs 变成了public
-
person 变成了 Person
-
Person 的所有属性都使用 Pascal 套管
让我们来玩一些示例数据
List<CPDocs> cd = new()
{
new() { DocumentDetails = "{\"name\":\"John\", \"age\":30, \"car\":null}", IsApproved = false },
new() { DocumentDetails = "{\"name\":\"Jane\", \"age\":27, \"car\":null}", IsApproved = true },
new() { DocumentDetails = "{\"name\":\"Doe\", \"age\":null, \"car\":null}", IsApproved = false },
};
天真的方法
var result = from doc in cd
let semiParsed = JObject.Parse(doc.DocumentDetails)
select new Person
{
Name = (string)semiParsed[nameof(Person.Name).ToLower()],
Age = (string)semiParsed[nameof(Person.Age).ToLower()],
Car = (string)semiParsed[nameof(Person.Car).ToLower()],
IsApproved = doc.IsApproved
};
foreach (var item in result)
Console.WriteLine($"{item.Name} ({item.Age}): '{item.Car}' {item.IsApproved}");
- 我在迭代过程中引入了一个名为
semiParsed 的临时变量来存储半解析的 json
- 使用
select 语句,我为每个doc 创建了一个新的Person
-
semiParsed 索引运算符需要字段名称
- 因为我们在 C# 对象中使用了 Pascal 大小写,在 json 中使用了骆驼大小写,这就是为什么我们需要在它们之间定义一个转换
nameof(Person.Age).ToLower()
- 索引运算符返回一个
JToken,它需要是converted 到string
(位)更高级的方法
上面的代码很难维护,因为它是重复的 ((string)semiParsed[nameof(Person....).ToLower()])。
如果 json 字段和 C# 属性名称之间的映射不是那么直接,该怎么办?
让我们从select 语句中单独定义一个映射
Dictionary<string, string> mapping = new()
{
{ nameof(Person.Name), "name" },
{ nameof(Person.Age), "age" },
{ nameof(Person.Car), "car" },
};
当然,如果您可以控制 Person 类,那么您可能更喜欢 JsonPropertyAttribute 而不是这个自定义映射。
我们还可以定义一个函数(或 local function),它接收半解析的 json 和一个属性选择器(p => p.Name)并完成所有的魔法
string GetValueBasedOnSelector(JObject source, Expression<Func<Person, string>> propSelector)
{
var expression = (MemberExpression)propSelector.Body;
var propName = expression.Member.Name;
var fieldName = mapping[propName];
return (string)source[fieldName];
};
有了这个,Linq 查询看起来像这样:
result = from doc in cd
let semiParsed = JObject.Parse(doc.DocumentDetails)
select new Person
{
Name = GetValueBasedOnSelector(semiParsed, p => p.Name),
Age = GetValueBasedOnSelector(semiParsed, p => p.Age),
Car = GetValueBasedOnSelector(semiParsed, p => p.Car),
IsApproved = doc.IsApproved
};