3

I am trying to implement what seems like a simple JSON path filter, but failing to get it working. Wondering if others more experience with Json.NET's implementation of JSON path have ideas on next steps.

This scenario fails but I think should work?

var jsonText = @"{
    'event': {
        'data': {
            'intField': 1,
            'stringField': 'hello'
        }
    }
}";

JObject json = JsonConvert.DeserializeObject<JObject>(jsonText);
string jsonPath = "$.event.data[?(@.intField == 1)]";
IList<JToken> output = json.SelectTokens(jsonPath).ToList();

// this check fails
Assert.IsTrue(output.ToList().Count > 0);

If I massage the JSON payload by adding a dummy array around the 'data' object, then I can get the query working. However, I would rather not massage the JSON payload.

var jsonText = @"{
    'event': {
        'data': [{
            'intField': 1,
            'stringField': 'hello'
        }]
    }
}";

JObject json = JsonConvert.DeserializeObject<JObject>(jsonText);
string jsonPath = "$.event.data[?(@.intField == 1)]";
IList<JToken> output = json.SelectTokens(jsonPath).ToList();

// now this works
Assert.IsTrue(output.ToList().Count > 0);
dbc
  • 104,963
  • 20
  • 228
  • 340
Kauldric
  • 31
  • 2
  • I believe this may just be a limitation of Json.NET. See [Issue #1256: JSONPath scripts not executing correctly for objects](https://github.com/JamesNK/Newtonsoft.Json/issues/1256). It comes up here from time to time, e.g. [What's the correct JsonPath expression to search a JSON root object using Newtonsoft.Json.NET?](https://stackoverflow.com/q/49462187) and [How to filter a non-array in JsonPath](https://stackoverflow.com/q/43707780). You might want to add a comment to the issue on github if this functionality would be useful to you. – dbc Jul 26 '18 at 21:43
  • You might check to see if the workaround from [Newtonsoft JSON.Net SelectToken Issue](https://stackoverflow.com/q/39442443/3744182) works for you also. – dbc Jul 26 '18 at 21:46

1 Answers1

6

The problem is that Json.NET's implementation of the JsonPATH filter expression operator [?()] only works for filtering of objects inside collections (arrays) -- not objects inside other objects. This limitation is reported in Issue #1256: JSONPath scripts not executing correctly for objects, to which Newtonsoft replied,

I'm not sure about this. Nothing in the JSONPath says that filters should apply to objects.
JamesNK commented on Mar 24, 2017

This limitation comes up here from time to time, e.g. What's the correct JsonPath expression to search a JSON root object using Newtonsoft.Json.NET? or How to filter a non-array in JsonPath. You might want to add a comment to the issue on GitHub if you would find filtering for objects inside objects useful.

A workaround that sometimes does the job is to add the recursive descent operator .. between the containing object property name and the filter expression operator, like so:

string jsonPath = "$.event.data..[?(@.intField == 1)]";

And, in your specific case, the modified query now works and selects one object. Demo fiddle here.

Of course, this modified query will also match something like:

{
  "event": {
    "data": {
      "extra_added_level_of_nesting": {
        "intField": 1,
        "stringField": "hello"
      }
    }
  }
}

(fiddle #2 here) so the workaround may not be sufficient for your needs.

The workaround succeeds because, apparently, Json.NET considers the results returned by the recursive descent operator to be a collection, and thus can apply a filter expression operator to it.

Community
  • 1
  • 1
dbc
  • 104,963
  • 20
  • 228
  • 340