Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON.NET - Select All Objects

Tags:

json

c#

json.net

I am looking for a method to select ALL the objects in the JObject using Json.NET. So in short, if I had the following JSON:

{
  "someCar" : {
    "id" : "3",
    "model" : "M7",
    "engine" : "FI V8",
  },
  "name" : "carparkone",
  "id" : "1",
  "cars" : [
    {
      "id" : "1",
      "model" : "s60",
      "engine" : "i5",
    },
    {
      "id" : "2",
      "model" : "m3",
      "engine" : "FI V6",
    },
    {
      "id" : "3",
      "model" : "M7",
      "engine" : "FI V8",
    }
  ]
}

I would run some command to get an array of all the objects in it, i.e. anything in {} blocks.

Ideally, I would find all the objects where someProp has some value, so only objects that have a property engine with a value of V6.

tl;dr the question:

  1. How do I get a list of ALL objects nested in JObject?
  2. (Bonus) only get objects with specific properties.
like image 449
VSO Avatar asked Oct 06 '17 18:10

VSO


People also ask

What is JObject and JToken?

The JToken hierarchy looks like this: JToken - abstract base class JContainer - abstract base class of JTokens that can contain other JTokens JArray - represents a JSON array (contains an ordered list of JTokens) JObject - represents a JSON object (contains a collection of JProperties) JProperty - represents a JSON ...

What is SelectToken in c#?

SelectToken is a method on JToken and takes a string path to a child token. SelectToken returns the child token or a null reference if a token couldn't be found at the path's location. The path is made up of property names and array indexes separated by periods, e.g. Manufacturers[0]. Name.

What is Jsonconvert?

Provides methods for converting between . NET types and JSON types.

How do I get keys for JObject?

You can simply convert the JObject into a Dictionary object and access the method Keys() from the Dictionary object.


2 Answers

You can use LINQ to JSON to parse and filter JSON objects when there is no predefined schema.

First, parse your JSON to a JObject using JToken.Parse(). Then you can use JContainer.DescendantsAndSelf() to iterate through that root object, and all descendant tokens of it, in document order. (Or use JContainer.Descendants() if you want to skip the root object.) Then you can filter then using using .OfType<JObject>() to return all objects whether nested or not:

var root = JObject.Parse(jsonString;
var allObjs = root.DescendantsAndSelf()
    .OfType<JObject>()
    .ToArray();

To filter by some value, you can add an additional Where() clause as shown in the following extension method:

public static partial class JTokenExtensions
{
    public static JObject [] FilterObjects<T>(this JObject root, string someProp, T someValue)
    {
        var comparer = new JTokenEqualityComparer();
        var someValueToken = JToken.FromObject(someValue);
        var objs = root.DescendantsAndSelf()
            .OfType<JObject>()
            .Where(t => comparer.Equals(t[someProp], someValueToken))
            .ToArray();

        return objs;
    }
}

And then do:

var filteredObjs = root.FilterObjects(someProp, someValue);

To make FilterObjects() be completely generic, I serialize the desired value to a JToken then use JTokenEqualityComparer to compare the actual value with the desired value. If you know that the desired value is a primitive type, instead you can do:

public static partial class JTokenExtensions
{
    public static bool IsNull(this JToken token)
    {
        return token == null || token.Type == JTokenType.Null;
    }

    public static JObject[] FilterObjectsSimple<T>(this JObject root, string someProp, T someValue)
    {
        var comparer = EqualityComparer<T>.Default;
        var objs = root.DescendantsAndSelf()
            .OfType<JObject>()
            .Where(t => { var v = t[someProp]; return v != null && (someValue == null ? v.IsNull() : comparer.Equals(v.ToObject<T>(), someValue)); })
            .ToArray();

        return objs;
    }
}

Sample fiddle.

Note - you might also consider using SelectTokens(), which supports the JSONPath query syntax, e.g.:

var someProp = "id";
var someValue = "3";
var filterString = string.Format(@"..*[?(@.{0} == '{1}')]", someProp, someValue);
var filteredObjs = root.SelectTokens(filterString).ToArray();

However, your JSON includes objects nested directly inside other objects, and Newtonsoft's implementation of JSONPath does not find such directly nested objects, as explained in JSONPath scripts not executing correctly for objects #1256.

like image 82
dbc Avatar answered Oct 13 '22 18:10

dbc


You model your data like this:

public class Carpark {

    [JsonProperty(PropertyName = "name")]
    public string Name{ get; set; }

    [JsonProperty(PropertyName = "id")]
    public int Id {get; set;}

    [JsonProperty(PropertyName = "cars")]
    public IEnumerable<Car> Cars { get; set; }
}

public class Car {

    [JsonProperty(PropertyName = "id")]
    public int Id { get; set; }

    [JsonProperty(PropertyName = "model")]
    public string Model { get; set; }

    [JsonProperty(PropertyName = "engine")]
    public string Engine { get; set; }
}

Then use the model to Deserialize your string using Json.Net.

var carpark = JsonConvert.DeserializeObject<Carpark>(myJsonString);

foreach(var car in carpark.Cars.Where(c => c.Engine.ToLower().Contains("v6"))
    Console.WriteLine(car.Model);
like image 28
Kyle Avatar answered Oct 13 '22 18:10

Kyle