Newtonsoft.json: Unable to use JSONPath expressions like @.length

Created on 21 May 2017  路  9Comments  路  Source: JamesNK/Newtonsoft.Json

I am currently working on a project where users can add JSONPath strings to a configuration file to pull specific property values from a JSON object. Currenlty, I am running into a situation where I would like to pull out a property from the last item in an array. For demonstration purposes, I am using this "store" JSON object.

{
    "store" : {
        "book" : [{
                "category" : "reference",
                "author" : "Nigel Rees",
                "title" : "Sayings of the Century",
                "price" : 8.95
            }, {
                "category" : "fiction",
                "author" : "Evelyn Waugh",
                "title" : "Sword of Honour",
                "price" : 12.99
            }, {
                "category" : "fiction",
                "author" : "Herman Melville",
                "title" : "Moby Dick",
                "isbn" : "0-553-21311-3",
                "price" : 8.99
            }, {
                "category" : "fiction",
                "author" : "J. R. R. Tolkien",
                "title" : "The Lord of the Rings",
                "isbn" : "0-395-19395-8",
                "price" : 22.99
            }
        ],
        "bicycle" : {
            "color" : "red",
            "price" : 19.95
        }
    }
}

Now I would like to pull the author of the last book on the list using JSONPath and JObject.SelectToken() only. Looking around at available documentation on JSONPath (http://goessner.net/articles/JsonPath/), the solution would be to use an expression like [(@.length-1)] to pull the final result in a JSON array using only a JSONPath string. Below is a code example of what I would like to do assuming the JSON object listed above is contained within the string variable 'json'.

Sample C# Code / Steps to reproduce.

string jsonPath = "$.store.book[(@.length-1)].author";

JObject jobj = JObject.Parse(json);

string output = jobj.SelectToken(jsonPath).ToString();

Expected behavior

I would expect the output variable to contain a value of "J. R. R. Tolkien".

Actual behavior

Instead, I receive the following exception.

Unexpected character while parsing path indexer: (
at Newtonsoft.Json.Linq.JsonPath.JPath.ParseArrayIndexer(Char indexerCloseChar)
at Newtonsoft.Json.Linq.JsonPath.JPath.ParseIndexer(Char indexerOpenChar, Boolean scan)
at Newtonsoft.Json.Linq.JsonPath.JPath.ParsePath(List`1 filters, Int32 currentPartStartIndex, Boolean query)
at Newtonsoft.Json.Linq.JsonPath.JPath.ParseMain()
at Newtonsoft.Json.Linq.JsonPath.JPath..ctor(String expression)
at Newtonsoft.Json.Linq.JToken.SelectToken(String path, Boolean errorWhenNoMatch)
at Newtonsoft.Json.Linq.JToken.SelectToken(String path)

Is Newtonsoft.Json capable of handling JSONPath expressions like these? If so, what are the best ways to go about doing this while only using a JSONPath string and the JObject.GetToken() method. If this library is not capable of handling these expressions, then I feel that this is a noted feature gap and should be addressed in future releases.

Most helpful comment

Any update of the support of the length function ?

All 9 comments

@.length on arrays isn't supported.

I believe $..book[-1:] should work.

That worked perfectly, thank you James!

Are there any plans to add the use of expressions like @.length on the road-map for future releases? While this work-around is equally as effective, adding the functionality above would allow this library to better conform to JSONPath standards, offering users the full suite of expected JSONPath functionality.

Thank you once again for your help. I am closing out this ticket.

Reopen this issue and it can be closed when @.length is supported.

JamesNK,

How in the heck did you know to put [-1:] to retrieve the last item in an index? You know, other than you wrote the damn thing.

Where can I find documentation that houses this information? Reason why I ask is because I've been scouring the interwebs for examples of how to do this and came up empty handed. What most folks seem to receive in terms of syntax is located here:

http://goessner.net/articles/JsonPath/index.html#e2

And for anyone else struggling with this, the above solution that James posted works perfectly.

Thanks,

The page you linked to has ity as an example:

$..book[-1:]

Dang it. My eyes geared towards to that specific implementation and overlooked everything else.

Thanks,

Any update of the support of the length function ?

@NicolasHumann - just FYI, if you want to get count of it then you could do something like this:

var jsonPath = "$.book[*]";
var count = json.SelectTokens(jsonPath).Count();

What will happen if there was a field named length in the path of @ ?

I think the origin jsonpath creator actually leave mistake in document. This kind of things such as length should not be normal expression, it actually should be function

Hence it should be

C# string jsonPath = "$.store.book[(@.length() - 1)].author";

The implementation of jsonpath in java is also put some expression to be function, such as min along with length https://github.com/json-path/JsonPath#functions

But I have seen you said that it would be too complicate in #209

However I think we should be able to pull it off if we define it as function and checking with () syntax in the parser, even including custom function should be possible, shouldn't it?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bytenik picture bytenik  路  11Comments

schani picture schani  路  11Comments

plokin picture plokin  路  12Comments

TylerBrinkley picture TylerBrinkley  路  37Comments

davidfowl picture davidfowl  路  49Comments