0

I have JSON that looks like this: (and I do not control this data structure)

{  
   "Quest":"To seek the Holy Grail",
   "FavoriteColor":"Blue",
   "Mother":{  
      "name":"Eve",
      "dob":"1/1/1950"
   },
   "Father":{  
      "name":"Adam",
      "dob":"2/1/1950"
   },
   "Siblings":[  
      {  
         "name":"Abel",
         "dob":"1/1/1980"
      },
      {  
         "name":"Kain",
         "dob":"3/1/1981"
      }
   ]
}

I have written code to use the Newtonsoft JSON SelectToken method to look up the names of the mother, father, and siblings and print them on the screen:

using System;
using Newtonsoft.Json.Linq;

namespace JsonTest
{
    class Program
    {
        const string JSON = @"{  
           ""Quest"":""To seek the Holy Grail"",
           ""FavoriteColor"":""Blue"",
           ""Mother"":{  
              ""name"":""Eve"",
              ""dob"":""1/1/1950""
           },
           ""Father"":{  
              ""name"":""Adam"",
              ""dob"":""2/1/1950""
           },
           ""Siblings"":[  
              {  
                 ""name"":""Abel"",
                 ""dob"":""1/1/1980""
              },
              {  
                 ""name"":""Kain"",
                 ""dob"":""3/1/1981""
              }
           ]
        }";
        static void Main(string[] args)
        {
            JObject jObject = JObject.Parse(JSON);           

            JToken mother = jObject.SelectToken("Mother");
            JToken father = jObject.SelectToken("Father");
            JToken siblings = jObject.SelectToken("Siblings");

            Console.WriteLine("Mother: " + mother.ToString());
            Console.WriteLine("Father: " + father.ToString());
            Console.WriteLine("Siblings: " + siblings.ToString());
        }
    }
}

I pass three different arguments to SelectToken to select three different parts of the document into three different JToken variables. It should be noted that two of those variables contain single names, but the last contains an array of names.

I have been asked to do a task where I will need the values for Mother, Father, and Siblings all in one array.

In short, I want to write something like this:

JToken family = jObject.SelectToken("_____");
Console.WriteLine(family.ToString());

and the result should be this:

[  
   {  
      "name":"Eve",
      "dob":"1/1/1950"
   },
   {  
      "name":"Adam",
      "dob":"2/1/1950"
   },
   {  
      "name":"Abel",
      "dob":"1/1/1980"
   },
   {  
      "name":"Kain",
      "dob":"3/1/1981"
   }
]

Is there a value I can fill in the blank for SelectToken to make this happen? I already have a system where data is selected with a single call to SelectToken, so it will make things much easier if I don't have to write in an exception to make multiple calls.

Vivian River
  • 31,198
  • 62
  • 198
  • 313
  • 1
    I don't *think* so. There's no general "OR" operator in [JSONPath](http://goessner.net/articles/JsonPath/) and, from experimentation, the `?()` filter syntax only filters objects inside arrays not objects inside other objects. Thus `jObject.SelectTokens("..*.[?(@.name && @.dob)]")` only finds Abel and Kain. `jObject.SelectTokens("..[?(@.name && @.dob)]")` finds nothing. – dbc Dec 03 '15 at 18:30
  • 1
    It says here (http://goessner.net/articles/JsonPath/) that there is a union operator [,], but I haven't been able to make it work. – Vivian River Dec 03 '15 at 18:32
  • 1
    That's a union operator for indexing a particular node, not a general purpose union operator. Thus `jObject.SelectTokens("..['Mother','Father','Siblings']")` works, but returns 3 entries: the mother, the father, and an array for `Siblings`. I don't see a way to flatten the `Siblings` array but not try to flatten the `Mother` and `Father` nodes. – dbc Dec 03 '15 at 18:57
  • I think I'm getting close. I tried `jObject.SelectTokens("..['Mother','Father','Siblings']..*")`. This gets every possible sub-selection, which includes the four people I want, but it also includes the array, plus the individual names and birthdates outside of the objects. I think I need one more sub-selector to tell it to only return objects with a `name` and `dob` – Vivian River Dec 03 '15 at 19:12
  • 1
    But unfortunately `?()` only filters array contents, so `jObject.SelectTokens("['Mother','Father','Siblings']..*.[?(@.name && @.dob)]")` only returns Abel and Kain. – dbc Dec 03 '15 at 19:23
  • May it then be possible to treat the results of ['Mother','Father','Siblings']..* as an array and get all four people selected from that array? – Vivian River Dec 03 '15 at 19:31
  • You can do `jObject.SelectTokens("..['Mother','Father','Siblings']").SelectMany(t => t.Type == JTokenType.Array ? t.AsEnumerable() : new[] { t })`, but it doesn't quite match your requirement of doing the query with a single string. – dbc Dec 03 '15 at 19:54

0 Answers0