Search code examples
c#jsonjson.netjsonpath

How to query multiple fields with SelectTokens?


If I've got a JSON array and I want to extract one field from each object, it's fairly simple:

Data:
{ 
  "Values": [
    {
      "Name": "Bill",
      "Age": "25",
      "Address": "1234 Easy St."
    },
    {
      "Name": "Bob",
      "Age": "28",
      "Address": "1600 Pennsylvania Ave."
    },
    {
      "Name": "Joe",
      "Age": "31",
      "Address": "653 28th St NW"
    }
  ]
}

Query:
data.SelectTokens("Values[*].Name")

This will give me an array of all the names. But what if I want more than one field? Is there any way to get an array of objects containing names and addresses?

The obvious way is to run SelectTokens twice and then Zip them, but will that work? Are the two result arrays guaranteed to preserve the ordering of the original source data? And is there a simpler way that can do it with just one query?


Solution

  • You can use the union operator ['Name','Address'] to select the values of multiple properties simultaneously. However, at some point you're going to need to generate new objects containing just the required properties, for instance by grouping them by parent object:

    var query = data.SelectTokens("Values[*]['Name','Address']")
        .Select(v => (JProperty)v.Parent) // Get parent JProperty (which encapsulates name as well as value)
        .GroupBy(p => p.Parent)           // Group by parent JObject
        .Select(g => new JObject(g));     // Create a new object with the filtered properties
    

    While this works and uses only one JSONPath query, it feels a little overly complex. I'd suggest just selecting for the objects, then using a nested query to get the required properties like so:

    var query = data.SelectTokens("Values[*]")
        .OfType<JObject>()
        .Select(o => new JObject(o.Property("Name"), o.Property("Address")));
    

    Or maybe

    var query = data.SelectTokens("Values[*]")
        .Select(o => new JObject(o.SelectTokens("['Name','Address']").Select(v => (JProperty)v.Parent)));
    

    Demo fiddle here.