Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use JSONPath expressions like @.length #1318

Open
Ashatz opened this issue May 21, 2017 · 9 comments
Open

Unable to use JSONPath expressions like @.length #1318

Ashatz opened this issue May 21, 2017 · 9 comments

Comments

@Ashatz
Copy link

Ashatz commented May 21, 2017

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.

@JamesNK
Copy link
Owner

JamesNK commented May 21, 2017

@.length on arrays isn't supported.

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

@Ashatz
Copy link
Author

Ashatz commented May 22, 2017

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.

@Ashatz Ashatz closed this as completed May 22, 2017
@JamesNK
Copy link
Owner

JamesNK commented May 22, 2017

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

@JamesNK JamesNK reopened this May 22, 2017
@DDority
Copy link

DDority commented Jun 15, 2017

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,

@JamesNK
Copy link
Owner

JamesNK commented Jun 15, 2017

The page you linked to has ity as an example:

$..book[-1:]

@DDority
Copy link

DDority commented Jun 16, 2017

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

Thanks,

@NicolasHumann
Copy link

Any update of the support of the length function ?

@mdhalgara
Copy link

mdhalgara commented Jul 13, 2018

@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();

@Thaina
Copy link

Thaina commented Jan 13, 2019

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

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants