使用动态条件过滤 JSON 数组
Posted
技术标签:
【中文标题】使用动态条件过滤 JSON 数组【英文标题】:Filter JSON Array with dynamic conditions 【发布时间】:2022-01-10 21:56:30 【问题描述】:我有许多 JSON 数组,其中包含不同类型的节点。
示例 Json 1:
[
"EmpID": "23",
"EmpName": "Jhon",
"Age": "23"
,
"EmpID": "29",
"EmpName": "Paul",
"Age": "25"
,
"EmpID": "123",
"EmpName": "Jack",
"Age": "29"
,
"EmpID": "129",
"EmpName": "Apr",
"Age": "29"
]
示例 Json 2
[
"DepID": "2",
"Name": "Sales"
,
"DepID": "5",
"Name": "Marketing"
,
"DepID": "12",
"Name": "IT"
]
我想根据不同的条件过滤它们,例如
1)EmpID=29
这应该返回
[
"EmpID": "29",
"EmpName": "Paul",
"Age": "25",
]
2)Age=23 和 EmpName=Jhon
这应该返回
[
"EmpID": "23",
"EmpName": "Jhon",
"Age": "23"
]
-
年龄=29
这应该返回
[
"EmpID": "123",
"EmpName": "Jack",
"Age": "29"
,
"EmpID": "129",
"EmpName": "Apr",
"Age": "29"
]
所以我需要一种通用方法来对 JSON 数组执行任意数量的过滤器。我打算使用一些逗号分隔的字符串(例如Age="23",EmpName="Jhon"
)来获取所有过滤器,并且可以将其转换为代码中的任何格式。
我曾尝试使用 Json Path 创建动态过滤器,例如 $.[?(@.Age == '23' && @.EmpName == 'Jhon')]
。
我也尝试过使用 LINQ 之类的
var result = JsonConvert.DeserializeObject(jsonString);
var res = (result as Newtonsoft.Json.Linq.JArray).Where(x =>
x["Age"].ToString() =="23" && x["EmpName"].ToString()=="Jhon").ToList();
但是如何根据收到的任意数量的条件动态生成 where 条件
还有一个计划包括日期过滤器,以防json中有一些日期时间节点,例如BirthDate>12051995
。
我不确定如何使用任意数量的输入过滤条件进行动态过滤。
【问题讨论】:
我什么都做不到是什么意思? 为什么不使用 json.net 反序列化器? 反序列化 JSON --> 转换为对象列表 --> LINQ .Where() 过滤数据。 @YongShun 好的,我试过这个并且工作正常。 var res = (result as Newtonsoft.Json.Linq.JArray).Where(x => x["Age"].ToString() =="23" && x["EmpName"].ToString()=="Jhon ").ToList();但是如何根据任意数量的条件动态生成 where 条件 【参考方案1】:你几乎成功了。 :)
-
而不是使用
DeserializeObject
,然后将其转换为JArray
,更喜欢JArray.Parse
var json = File.ReadAllText("sample.json");
var semiParsedJson = JArray.Parse(json);
-
而不是在
Where
之后使用ToList
更喜欢JArray
构造函数,它可以很好地与IEnumerable<JToken>
一起工作
const string IdField = "EmpID", NameField = "EmpName", AgeField = "Age";
const StringComparison caseIgnorant = StringComparison.OrdinalIgnoreCase;
var idEq29 = semiParsedJson.Children()
.Where(token => string.Equals(token[IdField].Value<string>(),"29", caseIgnorant));
Console.WriteLine(new JArray(idEq29).ToString());
-
其他查询可以用同样的方式实现
var ageEq23AndNameJhon = semiParsedJson.Children()
.Where(token => string.Equals(token[AgeField].Value<string>(), "23", caseIgnorant)
&& string.Equals(token[NameField].Value<string>(), "Jhon", caseIgnorant));
Console.WriteLine(new JArray(ageEq23AndNameJhon).ToString());
var ageEq29 = semiParsedJson.Children()
.Where(token => string.Equals(token[AgeField].Value<string>(), "29", caseIgnorant));
Console.WriteLine(new JArray(ageEq29).ToString());
更新 #1:增强建议的解决方案 使用以下扩展方法
public static class JArrayExtensions
public static JArray Filter(this JArray array, Func<JToken, bool> predicate)
=> new JArray(array.Children().Where(predicate));
可以大大简化过滤
var idEq29 = semiParsedJson
.Filter(token => string.Equals(token[IdField].Value<string>(),"29", caseIgnorant));
var ageEq23AndNameJhon = semiParsedJson
.Filter(token => string.Equals(token[AgeField].Value<string>(), "23", caseIgnorant))
.Filter(token => string.Equals(token[NameField].Value<string>(), "Jhon", caseIgnorant));
var ageEq29 = semiParsedJson
.Filter(token => string.Equals(token[AgeField].Value<string>(), "29", caseIgnorant));
Console.WriteLine(idEq29);
Console.WriteLine();
Console.WriteLine(ageEq23AndNameJhon);
Console.WriteLine();
Console.WriteLine(ageEq29);
或者你可以把它推得更远。如果所有字段都存储字符串值,那么您可以像这样定义扩展方法:
public static class JArrayExtensions
public static JArray Filter(this JArray array, string field, string value)
=> new JArray(array.Children().Where(GenerateFilter(field, value)));
private static Func<JToken, bool> GenerateFilter(string field, string value)
=> (JToken token) => string.Equals(token[field].Value<string>(), value, StringComparison.OrdinalIgnoreCase);
过滤器查询非常简单:D
var idEq29 = semiParsedJson
.Filter(IdField,"29");
var ageEq23AndNameJhon = semiParsedJson
.Filter(AgeField, "23")
.Filter(NameField, "Jhon");
var ageEq29 = semiParsedJson
.Filter(AgeField, "29");
Console.WriteLine(ageEq23AndNameJhon);
Console.WriteLine();
Console.WriteLine(idEq29);
Console.WriteLine();
Console.WriteLine(ageEq29);
【讨论】:
【参考方案2】:要以传统方式进行这项工作,您需要执行 3 个步骤:
定义一个包含数据的类 将 json 反序列化为对象列表 使用 linq 查询您的选择您可以为部门做同样的事情。
如果您需要以任何方式加入他们,请使用.Join
。如果 JSON 是混合的,您可以创建一个包含所有属性的类并使用它进行查询。
所以对于简单的情况:首先定义一个类来代表你的对象:
public class Employee
public int EmpID get;set;
public string EmpName get;set;
public int Age get;set;
然后反序列化查询:
放在顶部:
using System.Text.Json;
public void Main()
//deserialize into a list
List<Employee> employees =
JsonSerializer.Deserialize<List<Employee>>(yourJsonString);
//query
var result = employees.Where(c => c.Age == 23 && c.EmpName == "Jhon");
//show results
foreach (var employee in result)
Console.WriteLine(employee.EmpID);
根据更新:
根据您的用例,您有几个选择:
固定数量的动态属性 真正的动态查询固定数量的动态属性
您可以通过以下方式实现更动态的设置:
//define the filterable properties
//note they are nullable
int? age = null;
int? id = null;
string name = null;
//apply them in a query
//
//note: if one of the filter properties is not set,
// that side of the && expression evaluates to "true"
var result = employees.Where(c => (age == null ? true : c.Age == age) &&
(id == null ? true : c.EmpId == id) &&
(name == null ? true : c.EmpName == name));
真正的动态查询
现在事情开始变得棘手。一种可能的选择是在 Dynamic Linq 之类的库的帮助下生成基于字符串的查询
【讨论】:
谢谢。但是我如何生成动态的 where 条件,我已经更新了更多细节的问题。 @techresearch:查看更新 另外,如果你有更多的问题超出了你原来的问题的范围,我建议你问一个新的问题;它会得到更多关注:-)以上是关于使用动态条件过滤 JSON 数组的主要内容,如果未能解决你的问题,请参考以下文章