Craft a JSONpath expression so that it retrieves only a specific value?
I have some JSON of which the following is a small sample:
{
"results": {
"div": [
{
"class": "sylEntry",
"div": [
{
"class": "sT",
"id": "sOT",
"p": "Mon 11/17, Computer work time"
},
{
"class": "des",
开发者_如何学C "id": "dOne",
"p": "All classes Siebel 0218"
}
],
"id": "sylOne"
}
]
}
}
I would like to only retrieve the "p"
content for the div element with class "sT"
. I would like to use a loop and doing something like this:
var arrayOfResults = $.results..div.p
does not work because I only want to retrieve the p value for the div element with class "sT"
.
So how do I construct my JSONpath so that it will retrive the array of p elements that are contained within the divs class "sT".
Thanks!!
Concepts
JSONPath apparently has a filter syntax that allows you to insert arbitrary Javascript into an expression for the purposes of matching or filtering. It also uses @
as a shortcut for the current node. Their example of combining these two things looks like this:
$..book[?(@.price<10)] // filter all books cheapier than 10
So this is probably what you want to use here.
Solution
To test the query I had in mind, I modified the jsonpath-test-js.html file in JSONPath's repo to test your data. You can copy-paste my sample to an HTML file and just load it in a browser.
Their test suite has an array of objects with fields called o
and p
. o
contains the original data to operate on while p
contains an array of JSONPath expressions to apply to o
. It loops over all these pairs and applies all the p
s to their respective o
s, printing out the result. Not as handy as a simple REPL, but it'll do.
Here's what I came up with:
<html>
<head>
<title> JSONPath - Tests (js)</title>
<script type="text/javascript" src="http://www.json.org/json.js"></script>
<script type="text/javascript"
src="http://jsonpath.googlecode.com/svn/trunk/src/js/jsonpath.js">
</script>
</head>
<body>
<pre>
<script type="text/javascript">
var out = "", tests =
[ { "o": { "results" : { "div" : [ { "clazz": "sylEntry",
"id": "sylOne", "div": [ { "clazz": "sT", "id": "sOT",
"p": "Mon 11/17, Computer work time" }, { "clazz": "des",
"id": "dOne", "p": "All classes Siebel 0218" } ] } ] } },
"p": ["$.results..div[?(@.clazz=='sT')].p", // my suggestion expression
"$.results..div[*].p"]}, // your question's expression
];
function evaluate($, p) {
var res = eval(p);
return res != null ? res.toJSONString() : null;
}
for (var i=0; i<tests.length; i++) {
var pathes;
for (var j=0; j<tests[i].p.length; j++) {
pre = ">";
if (pathes = jsonPath(tests[i].o, tests[i].p[j], {resultType: "PATH"}))
for (var k=0; k<pathes.length; k++) {
out += pre + " " + pathes[k] +
" = " + evaluate(tests[i].o, pathes[k]) + "\n";
pre = " ";
}
}
out += "<hr/>";
}
document.write(out);
</script>
</pre>
</body>
</html>
Note that this will first print the results of my query expression and then print the results of yours, so we can compare what they produce.
Here's the output it produces:
> $['results']['div'][0]['div'][0]['p'] = "Mon 11/17, Computer work time"
> $['results']['div'][0]['div'][0]['p'] = "Mon 11/17, Computer work time"
$['results']['div'][0]['div'][4]['p'] = "All classes Siebel 0218"
So the correct operator in the filter expression is ==
, meaning the correct expression for you is:
$.results..div[?(@.class=='sT')].p
However, I discovered one unfortunate issue (at least in the Javascript implementation of JSONPath): using the word 'class' in the above query results in this:
SyntaxError: jsonPath: Parse error: _v.class=='sT'
My only guess is that there's an eval
being called somewhere to actually evaluate the JSONPath expression. class
is a reserved word in Javascript, so it's causing issues. Let's try using the alternate syntax for @.class
:
$.results..div[?(@.['class']=='sT')].p
Results:
> $['results']['div'][0]['div'][0]['p'] = "Mon 11/17, Computer work time"
> $['results']['div'][0]['div'][0]['p'] = "Mon 11/17, Computer work time"
$['results']['div'][0]['div'][5]['p'] = "All classes Siebel 0218"
So use the above expression and you should be good to go! The filter feature looks powerful, so it'll probably be well worth exploring its capabilities!
Instead of using hard-to-grasp, non-standard query style, you could use DefiantJS (http://defiantjs.com), which extends the global object JSON with the method "search" - with which you can query JSON structures with standardised XPath queries. This method returns the matches in an array (empty array if no matches were found).
Here is a working JSfiddle of the code below;
http://jsfiddle.net/hbi99/sy2bb/
var data = {
"results": {
"div": {
"class": "sylEntry",
"id": "sylOne",
"div": [
{
"class": "sT",
"id": "sOT",
"p": "Mon 11/17, Computer work time"
},
{
"class": "des",
"id": "dOne",
"p": "All classes Siebel 0218"
}
]
}
}
},
res = JSON.search( data, '//div[class="sT"]/p' );
console.log( res[0] );
// Mon 11/17, Computer work time
To get an idea of XPath and how it works, check out this XPath Evaluator tool:
http://defiantjs.com/#xpath_evaluator
try this
JsonPath.with(jsonResponse).param("name", "getName").get("findAll { a -> a.name == name }")
精彩评论