我有一个要求,我试图通过过滤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查询中组合它们。单独查询正在运行。我需要"和"的语法。运营商。任何建议都会有所帮助。
答案 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中||
和&&
运算符的优先级。
因此,为了获得所需的查询,您有以下选择:
我从实验中发现,&&
和||
从左到右关联具有相同的优先级。因此,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();
您可以使用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();
您可以使用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();
顺便提一下,以下尝试无效:
尝试将逻辑运算符括起来会引发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();
在&&
错误选择与||
子句之一匹配但不符合||
子句的令牌后,&&
推送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
以下是显示工作和非工作查询的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);
}