使用动态条件过滤 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 数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 中使用字段名称、条件和值进行动态过滤

过滤具有相同 ID 和给定条件的对象的 json 数组

vuejs的动态过滤

对json数据进行过滤

在Javascript中按多个条件过滤数组

过滤数组并匹配至少一个条件