我正试图在我的一个项目中创建一个JSON过滤,这里的JSON是动态的,所以不能创建模型,过滤器将来自用户,我的示例JSON是
[
{
"id": 101,
"field1": "f1",
"field2": "f2",
"InnerArray": [
{
"id": 201,
"innerField1": "f1",
"innerField2": "f2"
},
{
"id": 202,
"innerField1": "f1",
"innerField2": "f2"
}
]
},
{
"id": 102,
"field1": "ff1",
"field2": "ff2",
"InnerArray": [
{
"id": 301,
"innerField1": "f1",
"innerField2": "f2"
},
{
"id": 302,
"innerField1": "f1",
"innerField2": "f2"
}
]
}
]
我正在尝试通过SelectToken((过滤它,除了内部数组外,它会很好地工作例如,如果查询是
string filter = "$.[?(@.id==101)]";
JToken filteredData = data.SelectToken($"{filter}");
//We will get
{
"id": 101,
"field1": "f1",
"field2": "f2",
"InnerArray": [
{
"id": 201,
"innerField1": "f1",
"innerField2": "f2"
},
{
"id": 202,
"innerField1": "f1",
"innerField2": "f2"
}
]
}
但是如果我想通过内部数组元素过滤JSOn,那么它将无法工作
string filter = "$.[?(@.InnerArray[?(@.id==301)])]";
JToken filteredData = data.SelectToken($"{filter}");
//Result is
{
"id": 102,
"field1": "ff1",
"field2": "ff2",
"InnerArray": [
{
"id": 301,
"innerField1": "f1",
"innerField2": "f2"
},
{
"id": 302,
"innerField1": "f1",
"innerField2": "f2"
}
]
}
我的期望是
{
"id": 102,
"field1": "ff1",
"field2": "ff2",
"InnerArray": [
{
"id": 301,
"innerField1": "f1",
"innerField2": "f2"
}
]
}
InnerArray Filter返回所有元素,内部JSON PATH不接受,有没有其他方法可以定义JSON路径?或者有任何替代方案可以动态过滤JSON,因为这里JSON将是动态的,过滤器将是动态
有可能,我已经构建了以下可执行代码来做到这一点:
为了使其可解析,否则JToken.Parse表示json不能作为数组启动。
string sourceFile = File.ReadAllText("./source.json");
JToken source = JToken.Parse(sourceFile);
List<JToken> tokensToRemove = source.SelectTokens("$..*[?(@.id == 101 || @.id == 301)]").ToList();
tokensToRemove.ForEach(t => t.Remove());
string result = source.ToString();
结果将符合您所说的预期。
FYI,$。。从父元素中选择所有元素,无论深度如何。
---编辑:
关于反其道而行之的后续问题。这是可能的,但你必须采取不同的做法。由于这些项位于源对象的不同级别,所以我认为最好构建一个新的JObject,其中包含一个想要的结果数组。
像这样:
string sourceFile = File.ReadAllText("./source.json");
JToken source = JToken.Parse(sourceFile);
List<JToken> tokensToKeep = source.SelectTokens("$..*[?(@.id == 101 || @.id == 301)]").ToList();
JObject resultObject = new JObject();
JArray array = new JArray();
resultObject.Add("array", array);
tokensToKeep.ForEach(t => array.Add(t));
string result = resultObject.ToString();
我认为JSONPath无法实现您的期望(如果我错了,请纠正我(。
$[?(@.InnerArray[?(@.id==301)])]
是指使用应用于子对象的InnerArray
属性的过滤器从父数组中选择标记。
所以在英语中,它的意思是:
给定父对象,如果其InnerArray
包含任何定义id
属性的对象,并且该id
属性的值等于301
,则返回父对象。
要从InnerArray
中选择令牌,您应该执行(假设id
是唯一的(:
$[?(@.InnerArray[?(@.id==301)])].InnerArray[0]
结果是:
[
{
"id":301,
"innerField1":"f1",
"innerField2":"f2"
}
]
然后,您需要替换所选父级中的InnerArray
:
var selectedInnerArray = obj.SelectToken(
"$[?(@.InnerArray[?(@.id==301)])].InnerArray[0]");
var selectedParent = obj.SelectToken("$[?(@.InnerArray[?(@.id==301)])]");
var result = selectedParent.DeepClone();
result["InnerArray"].Replace(new JArray(selectedInnerArray));
result
看起来像您的期望。
至于使用动态过滤器和JSON结构的通用过滤,我想我无法回答这个问题。您需要合并选定的令牌才能得到您的期望,我不知道如何轻松定义这些合并操作。
我认为创建一个模型是可能的。参见以下设计。
internal class Inner
{
public int id { get; set; }
public string innerField1 { get; set; }
public string innerField2 { get; set; }
}
internal class Outer
{
public int id { get; set; }
public string field1 { get; set; }
public string field2 { get; set; }
public List<Inner> InnerArray { get; set; }
}
下的该模型示例
private static void Main(string[] args)
{
List<Outer> list = new List<Outer>
{
new Outer()
{
id = 1, field1 = "f1",
field2 = "f2",
InnerArray = new List<Inner>()
{
new Inner()
{
id = 1,
innerField1="if1",
innerField2="if2"
}
}
},
new Outer()
{
id = 2,
field1 = "f1",
field2 = "f2",
InnerArray = new List<Inner>()
{
new Inner()
{
id = 1,
innerField1="if1",
innerField2="if2"
}
}
}
};
string serializedObject = JsonConvert.SerializeObject(list, Formatting.Indented);
Console.WriteLine(serializedObject);
Console.ReadLine();
}
在serializedObject中,您将获得json字符串,同样,您可以使用JsonConvert.DescializeObject(…(方法对字符串进行反序列化,然后使用Linq过滤出您的对象。
var deserializeList = JsonConvert.DeserializeObject<List<Outer>>(serializedObject);
var outer = deserializeList.FirstOrDefault(x => x.id == 1);
Console.WriteLine(outer?.id);
希望得到帮助。