如何创建包含 AND 和 OR 运算符的 JSONPath 过滤器表达式?

How can I create a JSONPath filter expression containing both AND and OR operators?

我有一个要求,我试图通过过滤 JSON 中对象的多个属性的存在 and/or 值来 select 字符串中的对象 JSON ]数组。

这是我的例子 JSON:

{'Fields':[{
  "Sub_Status": "Pending",
  "Status": "Pending",
  "Patient_Gender": "M"
}]}

我想使用这样的查询来检查这个 json 字符串(下面的查询是一个 SQL 查询)

string query = TRIM(UPPER(Status)) IN ('PENDING', 'DISPENSED','SHIPMENT') AND TRIM(Patient_Gender) IS NOT NULL

string json_string = {'Fields':[{
  "Sub_Status": "Pending",
  "Status": "Pending",
  "Patient_Gender": "M"
}]};

var test = json_string.Where(query).toString() /// a return of bool = true or false

我尝试使用 JSONPath 和 system.linq.dynamic,但没有成功。

更新

我需要 c# 中的响应。截至目前。我尝试使用 NewtonSoft.Json SelectToken 来 select 使用 JSON 路径查询的令牌。

我查询的第一部分是:

JToken test = s.SelectToken("$.Fields[?(@.Status == 'Pending' || @.Status == 'PENDING' || @.Status == 'SHIPMENT' || @.Status == 'DISPENSED')]");             

我查询的第二部分是:

JToken test = s.SelectToken("$.Fields[?(@.Patient_Gender != '')]");

问题出在 "and" 运算符上——我不知道如何将它们合并到一个 JSONPath 查询中。单独查询正在工作。我需要 "and" 运算符的语法。任何建议都会有所帮助。

实际上,您需要将 json 反序列化为一个或多个对象(如果有多个记录)并测试该对象。

转到您的解决方案资源管理器 > 右键单击​​ => 管理 Nuget 包 => 下载 Netwonsoft.Json,然后创建具有类似 json 结构的 class:

public class Case
{
    public string Sub_Status { set; get; }
    public string Status { set; get; }
    public string Patient_Gender { set; get; }
}

然后在你的程序中:

static void Main(string[] args)
{
    string json_string = @"{""Sub_Status"": ""Pending"",
                            ""Status"": ""Pending"",
                            ""Patient_Gender"": ""M""}";

    Case caseObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Case>(json_string);

    List<string> acceptedStatuses = new List<string> { "PENDING", "DISPENSED", "SHIPMENT" };

    bool test = statuses.Any(s => s.Equals(caseObj.Status, StringComparison.OrdinalIgnoreCase))
                            && !string.IsNullOrWhiteSpace(caseObj.Patient_Gender);
}

Newtonsoft 的 JSONPath 实现确实支持 AND 运算符,如 QueryExpression.cs 的 源代码所示 。它使用 && 语法。因此,如果您想要搜索具有 Status == 'Pending' 且具有 Patient_Gender 属性 的字段(具有任何值),您将执行:

var query = s.SelectTokens("$.Fields[?(@.Status == 'Pending' && @.Patient_Gender)]");

但是,您是 混合 &&|| 运算符,不幸的是,这两个运算符的优先顺序没有记录。 JSONPath website says, uselessly, that () is a script expression, using the underlying script engine. And the Newtonsoft documentation 除了将 reader 引用到同一个 JSONPath 网站之外什么也没说!

您可能想 open a documentation issue with Newtonsoft 要求他们澄清 JSONPath 中 ||&& 运算符的优先级。

因此,要获得您想要的查询,您有以下选择:

  1. 我从实验中发现,&&|| 以相同的优先级从左到右关联。因此 A && B || C 表示 A && (B || C)A || B && C 表示 A || (B && C)

    你显然想要前者。 只要您愿意依赖未记录的行为,以下查询应该有效:

    var filter = "$.Fields[?(@.Patient_Gender && @.Status == 'Pending' || @.Status == 'PENDING' || @.Status == 'SHIPMENT' || @.Status == 'DISPENSED')]";
    var query = s.SelectTokens(filter);
    var result = query.ToList();
    
  2. 可以使用Enumerable.Intersect()合并查询结果:

    var query1 = s.SelectTokens("$.Fields[?(@.Status == 'Pending' || @.Status == 'PENDING' || @.Status == 'SHIPMENT' || @.Status == 'DISPENSED')]");
    var query2 = s.SelectTokens("$.Fields[?(@.Patient_Gender)]");
    var query = query1.Intersect(query2);
    
    var result = query.ToList();
    
  3. 您可以使用 Enumerable.Where() 进行过滤,而只需使用 SelectTokens() 进行枚举:

    var query = from t in s.SelectTokens("$.Fields[*]")
                where (string)t["Patient_Gender"] != null
                let status = (string)t["Status"]
                where status == "Pending" || status == "PENDING" || status == "SHIPMENT" || status == "DISPENSED"
                select t;
    
    var result = query.ToList();
    

如果你只是想知道是否匹配,而不是匹配的标记列表,你可以使用 Any(),例如

var result = query.Any();

顺便说一句,以下尝试没有奏效

  1. 尝试将逻辑运算符括起来会引发 JsonException: Unexpected character while parsing path query: (:

    var filter = "$.Fields[?((@.Status == 'Pending' || @.Status == 'PENDING' || @.Status == 'SHIPMENT' || @.Status == 'DISPENSED') && (@.Patient_Gender))]";
    var query = s.SelectTokens(filter);
    var result = query.ToList();
    
  2. || 之后放置 && 错误地选择了匹配 || 子句之一但不匹配 && 子句的标记:

    var filter = "$.Fields[?(@.Status == 'Pending' || @.Status == 'PENDING' || @.Status == 'SHIPMENT' || @.Status == 'DISPENSED' && @.Patient_Gender)]";
    var query = s.SelectTokens(filter);
    var result = query.ToList();
    

这里是一个 fiddle 显示工作和非工作查询。