环境:
关于newtonsoft.json的使用常见问题参考:
《c#:序列化json常见问题及处理方法》
《c#:关于NewtonsoftJson序列化和Grpc序列化的冲突问题》
《c#: Newtonsoft.Json 高级用法》
比如有下面的json:
[
{
"name":"小明",
"books":[
{
"name":"明-语文",
"year":5
}
]
},
{
"name":"Tom",
"books":[
{
"name":"Tom-物理",
"year":3
}
]
}
]
直接上代码:
//使用c#11语法,当前需要将工程设置预览,即在.csproj的根目录下加入:
/*
preview
preview
*/
var str = """
[
{
"name":"小明",
"books":[
{
"name":"明-语文",
"year":5
}
]
},
{
"name":"Tom",
"books":[
{
"name":"Tom-物理",
"year":3
}
]
}
]
""";
var root = Newtonsoft.Json.JsonConvert.DeserializeObject<JArray>(str);
//提取所有的书籍名字呢
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(root.SelectTokens("$[*].books[*].name")));//输出: ["明-语文","Tom-物理"]
//提取year在3年以上的书籍名字
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(root.SelectTokens("$[*].books[?(@.year>3)].name")));//输出: ["明-语文"]
参考:
《Newtonsoft.Json文档: Querying JSON with JSON Path》
《JSONPath规则》
首先,我们需要明确:
在 newtonsoft 中我们可以将对象粗略的表示为下面两种类型:
JArray
表示;JObject
表示;它们都继承自:JToken
。
然后,我们看newtonsoft提供jsonpath的方法:
public IEnumerable
期待返回的是数组,所以:无论是否找到或找到几个都会返回一个数组
public JToken? SelectToken(string path):
期待返回的是非数组,所以:没找到返回null,找到1个就返回JToken,找到多个就报错!!!
有了上面的前提,我们来看jsonpath的语法介绍:
下面用一个实例介绍:
private static void TestBase()
{
var str = """
[
{
"name":"小明",
"age":20,
"books":[
{
"name":"明-语文",
"year":5,
"must":true
},
{
"name":"明-数学",
"year":2,
"must":false
}
]
},
{
"name":"小红",
"age":18,
"birth":"1998-01-01",
"books":[
{
"name":"红-政治",
"year":6,
"must":false
},
{
"name":"红-体育",
"year":4,
"must":false
}
]
},
{
"name":"Tom",
"age":22,
"birth":null,
"books":[
{
"name":"Tom-物理",
"year":6,
"must":"true"
}
]
}
]
""";
var root = str.ToObject<JArray>();
//输出原样
Console.WriteLine(root.SelectToken("$").ToJson());//output: 省略
//打印null,因为$是一个数组,它不是对象,没有name属性
Console.WriteLine(root.SelectToken($"$.name").ToJson());//output: null
//打印所有的name,包括人的姓名和书籍名称
Console.WriteLine(root.SelectTokens($"$..name").ToJson());//output: ["小明","明-语文","明-数学","小红","红-政治","红-体育","Tom","Tom-物理"]
//打印所有书籍的名称
Console.WriteLine(root.SelectTokens($"$..books..name").ToJson());//output: ["明-语文","明-数学","红-政治","红-体育","Tom-物理"]
//打印年龄小于20的人所有书籍名称
Console.WriteLine(root.SelectTokens($"$[?(@.age < 20)].books..name").ToJson());//output: ["红-政治","红-体育"]
//打印小明的所有书籍名称
Console.WriteLine(root.SelectTokens($"$[?(@.name == '小明')].books..name").ToJson());//output: ["明-语文","明-数学"]
//打印有birth属性的所有书籍名称
Console.WriteLine(root.SelectTokens($"$[?(@.birth)].books..name").ToJson());//output: ["红-政治","红-体育","Tom-物理"]
//打印有birth属性并且age>20的人的所有书籍名称
Console.WriteLine(root.SelectTokens($"$[?(@.birth && @.age>20)].books..name").ToJson());//output: ["Tom-物理"]
//打印有birth属性并且birth不为空的人的所有书籍名称
Console.WriteLine(root.SelectTokens($"$[?(@.birth && @.birth != '' && @.birth <>null)].books..name").ToJson());//output: ["红-政治","红-体育"]
//正则: 打印所有姓名"小"开头人的所有书籍名称
Console.WriteLine(root.SelectTokens($"$[?(@.name =~ /^小.+$/)].books..name").ToJson());//output: ["明-语文","明-数学","红-政治","红-体育"]
//打印拥有必读书籍的人的所有书籍名称: 注意 must 是完全相等,字符串 "true" 不行
Console.WriteLine(root.SelectTokens($"$[?(@.books[?(@.must===true)])].books..name").ToJson());//output: ["明-语文","明-数学"]
//打印所有人的所有书籍: 注意 是二维数组结构
Console.WriteLine(root.SelectTokens($"$..books").ToJson());//output: 省略
//打印所有人的所有书籍: 注意 是一维结构
Console.WriteLine(root.SelectTokens($"$..books[*]").ToJson());//output: 省略
//打印所有书籍名称
Console.WriteLine(root.SelectTokens($"$..books[*].name").ToJson());//output: ["明-语文","明-数学","红-政治","红-体育","Tom-物理"]
//数组切片 [start: end:step]
//打印最后一个人的最后一个书籍信息
Console.WriteLine(root.SelectTokens($"$[-1:].books[-1:]").ToJson());//output: [{"name":"Tom-物理","year":6,"must":"true"}]
//打印前两个人的最后一个书籍信息
Console.WriteLine(root.SelectTokens($"$[0,1].books[-1:]").ToJson());//output: [{"name":"明-数学","year":2,"must":false},{"name":"红-体育","year":4,"must":false}]
//打印第一个、第三个人的最后一个书籍信息(起始0,结束3,步长2,正好跳过中间的小红)
Console.WriteLine(root.SelectTokens($"$[0:3:2].books[-1:]").ToJson());//output: [{"name":"明-数学","year":2,"must":false},{"name":"Tom-物理","year":6,"must":"true"}]
//打印第一个人的最后一个书籍信息(从第一个到倒数第二个,不包含倒数第二个,所以就只能是第一个人(总共3个))
Console.WriteLine(root.SelectTokens($"$[0:-2].books[-1:]").ToJson());//output: [{"name":"明-数学","year":2,"must":false}]
}
//扩展方法: ToJson ToObject
public static class JsonExtensions
{
public static string ToJson(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
public static T ToObject<T>(this string str)
{
return JsonConvert.DeserializeObject<T>(str);
}
}
其中的切片语法: