【问题标题】:Filter JSON Array with dynamic conditions使用动态条件过滤 JSON 数组
【发布时间】: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"
    }
]
  1. 年龄=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 条件

标签: c# json json.net


【解决方案1】:

你几乎成功了。 :)

  1. 而不是使用DeserializeObject,然后将其转换为JArray,更喜欢JArray.Parse
var json = File.ReadAllText("sample.json");
var semiParsedJson = JArray.Parse(json);
  1. 而不是在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());
  1. 其他查询可以用同样的方式实现
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:查看更新
    • 另外,如果你有更多的问题超出了你原来的问题的范围,我建议你问一个新的问题;它会得到更多关注:-)
    猜你喜欢
    • 2018-04-19
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-12
    • 2022-11-30
    相关资源
    最近更新 更多