问题
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:
- How do I get a list of ALL objects nested in JObject?
- (Bonus) only get objects with specific properties.
回答1:
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.
回答2:
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);
来源:https://stackoverflow.com/questions/46611888/json-net-select-all-objects