json-path / JsonPath

Java JsonPath implementation
Apache License 2.0
8.94k stars 1.65k forks source link

Regression from 0.9.1 #70

Closed niclash closed 9 years ago

niclash commented 9 years ago

There is a regression in 1.2.0 from 0.9.1, where a complex input document and expression simply doesn't agree correctly.

The code is found below, and the exception shows that it misunderstands the Array situation of the "[0]" in "path2", although it clearly extracts a JSONArray in if I remove the "[0]" as seen in the code below.

Exception in thread "main" com.jayway.jsonpath.InvalidPathException: Filter: [0] can only be applied to arrays. Current context is: 51.968503937007874 at com.jayway.jsonpath.internal.token.ArrayPathToken.evaluate(ArrayPathToken.java:60) at com.jayway.jsonpath.internal.token.PathToken.handleObjectProperty(PathToken.java:71) at com.jayway.jsonpath.internal.token.PropertyPathToken.evaluate(PropertyPathToken.java:44) at com.jayway.jsonpath.internal.token.PathToken.handleArrayIndex(PathToken.java:125) at com.jayway.jsonpath.internal.token.PredicatePathToken.evaluate(PredicatePathToken.java:67)

import com.jayway.jsonpath.JsonPath;

public class JsonPathFailureTest
{
    public static void main( String[] args )
    {
        JsonPath path1 = JsonPath.compile( "$.blocks[?(@.name=='AQ')].edges[?(@.name=='value')].value" );
        Object result1 = path1.read( JSON );
        System.out.println( result1 );
        System.out.println( result1.getClass() );

        JsonPath path2 = JsonPath.compile( "$.blocks[?(@.name=='AQ')].edges[?(@.name=='value')].value[0]" );
        System.out.println( path2.read( JSON ) );
    }

    private static final String JSON =
        "{" +
        "    \"blocks\":[{\"edges\":[{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"expr\",\"type\":\"String\",\"value\":\"\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"k\",\"type\":\"Double\",\"value\":\"1.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"m\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"url\",\"type\":\"String\",\"value\":\"xnet://81/aq\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"value\",\"type\":\"Double\",\"value\":\"-1.0\",\"tracked\":true}],\"name\":\"regMode\",\"type\":\"AnalogOutput\"},{\"edges\":[{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"expr\",\"type\":\"String\",\"value\":\"\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"k\",\"type\":\"Double\",\"value\":\"1.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"m\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"url\",\"type\":\"String\",\"value\":\"xnet://81/dq\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"value\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false}],\"name\":\"DQ\",\"type\":\"AnalogOutput\"},{\"edges\":[{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"expr\",\"type\":\"String\",\"value\":\"\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"k\",\"type\":\"Double\",\"value\":\"1.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"m\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"url\",\"type\":\"String\",\"value\":\"xnet://81/i\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"value\",\"type\":\"Double\",\"value\":\"50.0\",\"tracked\":false}],\"name\":\"I\",\"type\":\"AnalogOutput\"},{\"edges\":[{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"expr\",\"type\":\"String\",\"value\":\"\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"k\",\"type\":\"Double\",\"value\":\"1.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"m\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"url\",\"type\":\"String\",\"value\":\"xnet://81/setp\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"value\",\"type\":\"Double\",\"value\":\"55.0\",\"tracked\":false}],\"name\":\"BV\",\"type\":\"AnalogOutput\"},{\"edges\":[{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"expr\",\"type\":\"String\",\"value\":\"\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"k\",\"type\":\"Double\",\"value\":\"1.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"m\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"out\",\"low\":null,\"name\":\"value\",\"type\":\"Double\",\"value\":\"55.5\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"url\",\"type\":\"String\",\"value\":\"xnet://81/ai\",\"tracked\":false}],\"name\":\"GT1\",\"type\":\"AnalogInput\"},{\"edges\":[{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"expr\",\"type\":\"String\",\"value\":\"\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"k\",\"type\":\"Double\",\"value\":\"1.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"m\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"url\",\"type\":\"String\",\"value\":\"xnet://81/p\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"value\",\"type\":\"Double\",\"value\":\"2.0\",\"tracked\":false}],\"name\":\"P\",\"type\":\"AnalogOutput\"},{\"edges\":[{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"expr\",\"type\":\"String\",\"value\":\"\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"k\",\"type\":\"Double\",\"value\":\"1.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"m\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"out\",\"low\":null,\"name\":\"value\",\"type\":\"Double\",\"value\":\"51.968503937007874\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"url\",\"type\":\"String\",\"value\":\"xnet://81/aq\",\"tracked\":false}],\"name\":\"AQ\",\"type\":\"AnalogInput\"},{\"edges\":[{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"expr\",\"type\":\"String\",\"value\":\"\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"k\",\"type\":\"Double\",\"value\":\"1.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"m\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"out\",\"low\":null,\"name\":\"value\",\"type\":\"Double\",\"value\":\"55.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"url\",\"type\":\"String\",\"value\":\"xnet://81/setp\",\"tracked\":false}],\"name\":\"actualSetp\",\"type\":\"AnalogInput\"},{\"edges\":[{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"expr\",\"type\":\"String\",\"value\":\"\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"k\",\"type\":\"Double\",\"value\":\"1.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"m\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"out\",\"low\":null,\"name\":\"value\",\"type\":\"Double\",\"value\":\"0.0\",\"tracked\":false},{\"high\":null,\"io\":\"in\",\"low\":null,\"name\":\"url\",\"type\":\"String\",\"value\":\"xnet://81/dq\",\"tracked\":false}],\"name\":\"actualDQ\",\"type\":\"AnalogInput\"}],\"connections\":[{\"from\":\"VVC.choice.output\",\"to\":\"SG-VV.DQ.value\",\"type\":\"Double\"}],\"name\":\"SG-VV\"}";

}
kallestenflo commented 9 years ago

This was actually a bug in releases prior to v1.0. I'm aware that some people depended on this and I understand that it is useful in some cases. The problem is that from a JSONPath perspective it does not make sense. The last part [0] in your path2 does not correspond to a path in the document. What would the result be if you executed the query with the AS_PATH_LISToption? It does not exist.

I am considering to add some kind of pipe support. Then the path would be:

"$.blocks[?(@.name=='AQ')].edges[?(@.name=='value')].value | [0]" or "$.blocks[?(@.name=='AQ')].edges[?(@.name=='value')].value -> [0]"

but I'm not there yet.

niclash commented 9 years ago

I see.... as clear as mud. To see if I understand this correctly; I assumed that every operator, worked on the "resultset" of the previous operator (left and possibly some precedence rules). And in reality, the operators work like filters over the document structure before the evaluation of each operator. Right?

niclash commented 9 years ago

Also, instead of "pipe" character, a "valueof()" would be better to allow precedence, example;

$($.blocks[?(@.name=='AQ')].edges[?(@.name=='value')].value)[0]
kallestenflo commented 9 years ago

Prior to version 1.0 it worked the way you described (operating on the previous resultset). The evaluation kept going until the path contained no more tokens. This was not efficient and has been reworked. Yes, the new evaluation could be described like that.

dornatsky commented 9 years ago

Consider the following example:

{ 
  "c": {
    "d1": {
      "url": [ "url1", "url2" ]
    },
    "d2": {
      "url": [ "url3", "url4","url5" ]
    }
  }
}

and the expression $.c.*.url[2]. It fails with an "index out of bounds" error even though there is one element that fits the path. Is that expected?

kallestenflo commented 9 years ago

@dornatsky it's a bug but it's not related to this issue. I have created a new issue #79.

antone-sb commented 8 years ago

@kallestenflo Have you introduced this pipe mechanism or something similar yet?

kallestenflo commented 8 years ago

No, not yet.