Json.NET JSONPath query not returning expected res

2020-02-01 08:36发布

问题:

I'm using Newtonsoft's Json.Net to select nodes from the following json:

{  
   "projects":[  
      {  
         "name":"Project 1",
         "client":{  
            "code":"ABC",
            "name":"Client 1"
         }
      },
      {  
         "name":"Project 2",
         "client":{  
            "code":"DEF",
            "name":"Client 2"
         }
      },
      {  
         "name":"Project 3",
         "client":{  
            "code":"GHI",
            "name":"Client 3"
         }
      }
   ]
}

The following c# snippet

//json is a JObject representation of the json listed above
var clients =  json.SelectTokens("$.projects[*].client");

Yields:

[  
   {  
      "code":"ABC",
      "name":"Client 1"
   },
   {  
      "code":"DEF",
      "name":"Client 2"
   },
   {  
      "code":"GHI",
      "name":"Client 3"
   }
]

Which is cool, now, what I'd like to do is filter by client code, I would think that

$.projects[*].client[?(@.code == 'DEF')]

would work, but I obviously don't understand the syntax well enough. This returns an empty list:

 var test1 =  json.SelectTokens("$.projects[*].client[?(@.code == 'DEF')]").ToList();

And the single token selector returns a null:

  var test2 =  json.SelectToken("$.projects[*].client[?(@.code == 'DEF')]");

I tried a few different configurations on https://jsonpath.curiousconcept.com/ and it seems my query syntax is indeed broken.

Using Flow Communications' JSONPath 0.3.4 implementation (on the above link) I can get the client using

$..[?(@.code == 'DEF')]

however, this syntax does not seem valid for Json.Net (nor the Goessner implementation on the same page).

Anyone see what I'm doing wrong?

回答1:

Json.NET's JSONPath parser allows the filter (script) expression [?( )] to query for nested properties inside child objects of the candidate array item(s). Thus

var test = json.SelectTokens(@"$.projects[?(@.client.code == 'DEF')].client");

returns, as desired,

[
  {
    "code": "DEF",
    "name": "Client 2"
  }
]

Working .Net fiddle.

Upon experimentation it seems none of the JSONPath implementations at jsonpath.curiousconcept.com support this syntax, however it works correctly at jsonpath.com which uses https://github.com/ashphy/jsonpath-online-evaluator. The JSONPath proposal simply states that () is a script expression, using the underlying script engine which clearly leaves some room for interpretation as to whether and how this "should" work.

The filters used in the question apparently do not work with Json.NET because, as of version 10.0.3, it only officially supports applying filters to array items and not directly to objects -- see Issue #1256: JSONPath scripts not executing correctly for objects for a discussion. (Though sometimes cunning workarounds can be found, for instance in this answer.) Using nested properties inside filters, however, seems to be intended to work (and does work).