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

时间:2016-07-06 20:27:41

标签: c# json linq json.net jsonpath

我有一个要求,我试图通过过滤JSON数组中对象的多个属性的存在和/或值来选择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来使用JSONPath查询选择令牌。

截至目前我的查询的第一部分是:

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

我的查询的第二部分是:

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

问题在于"和" operator - 我不知道如何在单个JSONPath查询中组合它们。单独查询正在运行。我需要"和"的语法。运营商。任何建议都会有所帮助。

2 个答案:

答案 0 :(得分:2)

Newtonsoft的JSONPath实现确实支持AND运算符,如source for QueryExpression.cs 中所示。它使用&&语法。因此,如果您要搜索具有Status == 'Pending'并且存在Patient_Gender属性(包含任何值)的字段,您可以这样做:

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

但是,您混合 &&||运算符,遗憾的是这两个运算符的优先顺序没有记录。 JSONPath website无用地说()是一个脚本表达式,使用底层脚本引擎。 Newtonsoft documentation除了引用读者之外什么也没说那个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();
    
  4. 如果您只是想知道是否匹配,而不是匹配的令牌列表,您可以使用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();

         store=c(rep(8,4),rep(11,4))
      start=c("20131009","20131113","20131204","20150624","20140820","20140924","20150923","20151014")
      end=c("20131016","20131120","20131211","20150701","20140827","20141001","20150930","20151021")
      
      maint=data.frame(store,start,end)
      
      
      
      maint$start=as.Date(maint$start,"%Y%m%d")
      maint$end=as.Date(maint$end,"%Y%m%d")
      maint
        store    start      end
      1     8 20131009 20131016
      2     8 20131113 20131120
      3     8 20131204 20131211
      4     8 20150624 20150701
      5    11 20140820 20140827
      6    11 20140924 20141001
      7    11 20150923 20150930
      8    11 20151014 20151021
      
    3. 以下是显示工作和非工作查询的fiddle

答案 1 :(得分:1)

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

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

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);
}