Querying JSON Objects

May 27, 2009 at 8:54 PM

Hello,

I have an application that I am converting to .Net and the application makes heavy use of JSON. One thing that the current application does is it passes javascript markup in a variables that it then uses to query a JSON object. i.e.:

var myJSON ={ Field1:"Field1", SubObject1: { Field1 : "FirstField", Array1 : [ "Zero", "One", "Two" ] } };
var myQuery1 = "Field1";
var myQuery2 = "SubObject1.Array1[2]";

var Temp1 = QueryJson(myJSON, myQuery1);//"FirstField"
var Temp2 = QueryJson(myJSON, myQuery2);//"Two"

This isn't the exact syntax but the general idea is the same. I want to know if there is an anlogous function in the JSON.net framework? i.e.:

JObject myJSON = new JObject();
... //build the object
   //myJSON = { Field1:"Field1", SubObject1: { Field1 : "FirstField", Array1 : [ "Zero", "One", "Two" ] } }

string Temp1 = myJSON.QueryJson("SubObject1.Array1[2]");//"Two"

I know this may be asking a lot but with the these style of queries will not go away in the project I'm working on, so before I write my own function to parse the query I was wondering if there was existing functionality that could do this or atleast a easy way to do this that I might be overlooking.

Thanks,
aromero78

Coordinator
May 27, 2009 at 11:47 PM

No there isn't anything like that in Json.NET I'm afraid.

It would be useful to be sure but it is not on my radar at the moment. If you do implement anything around this area yourself I'd be interested at taking a look at your code.

Coordinator
May 27, 2009 at 11:48 PM

Actually one idea might be to use DataBinder.Eval. JObject and JArray are just dictionary/lists and DataBinder.Eval might be able to extract data from them.

May 28, 2009 at 3:05 PM
Edited May 28, 2009 at 3:33 PM

I took a stab at it, it may need to be refined a bit but it seems to work, let me know what you think:

public static JToken QueryJson(JToken JsonData, string Query){
            JToken TempData = JsonData;
            Regex QueryRegex = new Regex("((?:\\[(?:'|\")?)?([\\sA-Za-z0-9_\\-!~`@#$%^&*()+=]+)(?:(?:'|\")?\\])?\\.?)+", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            Match QueryMatch = QueryRegex.Match(Query);
            if (QueryMatch.Success)
            {
                for (int i = 0; i < QueryMatch.Groups[2].Captures.Count; i++)
                {
                    string Key = QueryMatch.Groups[2].Captures[i].Value;
                    int IntKey = int.MinValue;
                    if (int.TryParse(Key, out IntKey))
                        TempData = TempData[IntKey];
                    else
                        TempData = TempData[Key];
                    if (TempData == null) return null;
                }
                return TempData;
            }
            else return null;
        }

Basically I use a regular expression to break the query up into individual keys and then iterate through the list of keys and pass the key to the items collection of the JToken.

The one thing that might need to be tweaked right now would be the way the regex matches quotes, right now it considers any single or double quote as the start and end delimiter of the key. It would be better if the end quote was forced to match the same type of quote that the key started with. i.e.: "test'in" right now will match "test" and not "test'in". I tried using named groups but in the end I figured this would be good enough for what I have to do.

Nov 17, 2009 at 11:28 PM

Your QueryJson function works really well. Do you have an update for your regular expression to fix the quote issue? I can try and do it myself, but figured I'd ask before I spend time on it. Thanks!

Coordinator
Nov 18, 2009 at 12:46 AM
Edited Nov 18, 2009 at 5:37 AM

The latest version of the Json.NET source code in CodePlex has a SelectToken method on JToken. I think it does what you're looking for.

JObject o = JObject.Parse("{'People':[{'Name':'Jeff'},{'Name':'Joe'}]}");

// get name token of first person and convert to a string
string name = (string)o.SelectToken("People[0].Name");


Or if you wanted to select multiple values:

JObject o = JObject.Parse("{'People':[{'Name':'Jeff','Roles':['Manager', 'Admin']}]}");

// get role array token of first person and convert to a list of strings
IList<string> names = (string)o.SelectToken("People[0].Roles").Select(t => (string)t).ToList();

Nov 18, 2009 at 2:42 PM

That's awesome. Thanks so much! Works great!

Jul 30, 2010 at 3:27 PM

James -

In your first example, could you use SelectToken Method to put the names into a string array? 

Given this JSON (from your first example)

JObject o = JObject.Parse("{'People':[{'Name':'Jeff'},{'Name':'Joe'}]}");

How would you get a list of People's names?

I was thinking something along these lines: (This doesn't work - but I'm hoping theres another easy way to do this)

JObject o = JObject.Parse("{'People':[{'Name':'Jeff'},{'Name':'Joe'}]}");

// would like to get the names of the person listed in the JSON
IList<string> firstnames = o.SelectToken("People.Name").Select(t => (string)t).ToList();