Reputation: 13292
I suppose this is another entry in my series of questions, but I'm stuck again. This time, I'm having trouble working with a JArray of JObjects and determining the Property.Value type for each element in the JArray...
My code is here: https://dotnetfiddle.net/bRcSAQ
The difference between my previous questions and this question is that my outer Linq query gets both JObject and JArray tokens, so that's why I have an if (jo is JObject)
at Line 40 and a if (jo is JArray)
at Line 48.
Once I know I have a JArray of <JObjects>
, I have code that looks like this (Lines 48-):
if (jo is JArray)
{
var items = jo.Children<JObject>();
// return a JObject object
}
When I use a debugger and look at items, I see that it contains 3 JObject objects--one for Item_3A1, Item_3A2, and Item3A3. But I need to know the JTokenType for each JProperty.Value because I am interested only in Property values of type JTokenType.String.
So I tried:
// doesn't work :(
var items = jo.Children<JObject>()
.Where(p => p.Value.Type == JTokenType.String);
The compiler red-lines the Value property with the error CS0119 'JToken.Value<T>(object)' is a method, which is not valid in the given context.
I realize that "p" in the Linq express is not a JProperty. I guess it's a JObject. And I don't know how to cast "p" so that I can examine the type of JProperty object it represents.
Ultimately, I need the code for JArray processing (starting at Line 48) to add an return a JObject that contains an JSON array composed only of JProperty objects of type JTokenType.String. This means that given the sample JSON, it first should return a JObject holding these JSON properties:
{ ""Item_3A1"": ""Desc_3A1"" },
{ ""Item_3A2"": ""Desc_3A2"" },
{ ""Item_3A3"": ""Desc_3A3"" }
On the next iteration, it should return a JObject holding these JSON properties (notice that the nested Array3B1 properties are omitted because Array3B1 is not a JProperty with a Value type of JTokenType.String):
{ ""Item_3B1"": ""Desc_3B1"" },
{ ""Item_3B2"": ""Desc_3B2"" },
The third iteration would contain:
{ ""Item_3B11"": ""Desc_3B11"" },
{ ""Item_3B12"": ""Desc_3B12"" },
{ ""Item_3B13"": ""Desc_3B13"" }
And the fourth (final) iteration would contain:
{ ""Item_3C1"": ""Desc_3C1"" },
{ ""Item_3C2"": ""Desc_3C2"" },
{ ""Item_3C3"": ""Desc_3C3"" }
This might be my final hurdle in this "series".
Sincere thanks to anyone who can and will help--and a special thanks again to users "Brian Rogers" and "dbc" for their truly amazing JSON.NET/Linq knowledge.
Upvotes: 4
Views: 16624
Reputation: 116991
This produces the output you require:
var root = (JContainer)JToken.Parse(json);
var query = root.Descendants()
.Where(jt => (jt.Type == JTokenType.Object) || (jt.Type == JTokenType.Array))
.Select(jo =>
{
if (jo is JObject)
{
if (jo.Parent != null && jo.Parent.Type == JTokenType.Array)
return null;
// No help needed in this section
// populate and return a JObject for the List<JObject> result
// next line appears for compilation purposes only--I actually want a populated JObject to be returned
return new JObject();
}
if (jo is JArray)
{
var items = jo.Children<JObject>().SelectMany(o => o.Properties()).Where(p => p.Value.Type == JTokenType.String);
return new JObject(items);
}
return null;
})
.Where(jo => jo != null)
.ToList();
Here I use SelectMany()
to flatten the nested enumeration of properties of the enumeration of child objects of jo
to a single enumeration of all properties of child objects. o => o.Properties()
is a lambda expression mapping the JObject o
to its collection of properties, and p => p.Value.Type == JTokenType.String
is another lambda mapping a property p
(generated by the previous SelectMany
clause) to a true/false value indicating whether the property has a string value. Both o
and p
are lambda input parameters that are implicitly typed.
Also, in the // No help needed in this section
section, objects whose parents are arrays are skipped, since they will get collected by the (jo is JArray)
clause.
Note that if different children of the jo
array happen to have identical property names, the JObject
constructor may throw a duplicated key exception.
Forked fiddle.
Upvotes: 4