I have a JObject which is used as a template for calling RESTful web services. This JObject gets created via a parser and since it's used as a template telling the user what the endpoint schema looks like, I had to figure out a way to preserve all properties, which is why I'm defaulting their values to null. As as example, this is what the object originally looks like:
{
"Foo":{
"P1":null,
"P2":null,
"P3":null,
"P4":{
"P1":null,
"P2":null,
"P3":null,
},
"FooArray":[
{
"F1":null,
"F2":null,
"F3":null,
}
]
},
"Bar":null
}
The user is then able to fill in individual fields as they need, such as Foo.P2 and Foo.P4.P1:
{
"Foo":{
"P1":null,
"P2":"hello world",
"P3":null,
"P4":{
"P1":1,
"P2":null,
"P3":null,
},
"FooArray":[
{
"F1":null,
"F2":null,
"F3":null,
}
]
},
"Bar":null
}
meaning they only care about those two fields. Now I want to serialize this template (JObject) back to a JSON string, but want only those fields that are populated to show up. So I tried this:
string json = JsonConvert.SerializeObject(template,
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
Unfortunately, this didn't work. I came across this question and realized that a null value in the object is an actual JToken type and not really a null, which makes sense. However, in this very particular case, I need to be able to get rid of these "unused" fields. I tried manually iterating over nodes and removing them but that didn't work either. Note that the only managed type I'm using is JObject; I don't have a model to convert the object to or define attributes on, since this "template" gets resolved at runtime. I was just wondering if anyone has encountered a problem like this and has any insights. Any help is greatly appreciated!
You can use a recursive helper method like the one below to remove the null values from your JToken hierarchy prior to serializing it.
using System;
using Newtonsoft.Json.Linq;
public static class JsonHelper
{
public static JToken RemoveEmptyChildren(JToken token)
{
if (token.Type == JTokenType.Object)
{
JObject copy = new JObject();
foreach (JProperty prop in token.Children<JProperty>())
{
JToken child = prop.Value;
if (child.HasValues)
{
child = RemoveEmptyChildren(child);
}
if (!IsEmpty(child))
{
copy.Add(prop.Name, child);
}
}
return copy;
}
else if (token.Type == JTokenType.Array)
{
JArray copy = new JArray();
foreach (JToken item in token.Children())
{
JToken child = item;
if (child.HasValues)
{
child = RemoveEmptyChildren(child);
}
if (!IsEmpty(child))
{
copy.Add(child);
}
}
return copy;
}
return token;
}
public static bool IsEmpty(JToken token)
{
return (token.Type == JTokenType.Null);
}
}
Demo:
string json = #"
{
""Foo"": {
""P1"": null,
""P2"": ""hello world"",
""P3"": null,
""P4"": {
""P1"": 1,
""P2"": null,
""P3"": null
},
""FooArray"": [
{
""F1"": null,
""F2"": null,
""F3"": null
}
]
},
""Bar"": null
}";
JToken token = JsonHelper.RemoveEmptyChildren(JToken.Parse(json));
Console.WriteLine(token.ToString(Formatting.Indented));
Output:
{
"Foo": {
"P2": "hello world",
"P4": {
"P1": 1
},
"FooArray": [
{}
]
}
}
Fiddle: https://dotnetfiddle.net/wzEOie
Notice that, after removing all null values, you will have an empty object in the FooArray, which you may not want. (And if that object were removed, then you'd have an empty FooArray, which you also may not want.) If you want to make the helper method more aggressive in its removal, you can change the IsEmpty function to this:
public static bool IsEmpty(JToken token)
{
return (token.Type == JTokenType.Null) ||
(token.Type == JTokenType.Array && !token.HasValues) ||
(token.Type == JTokenType.Object && !token.HasValues);
}
With that change in place, your output would look like this instead:
{
"Foo": {
"P2": "hello world",
"P4": {
"P1": 1
}
}
}
Fiddle: https://dotnetfiddle.net/ZdYogJ
You can prevent the null tokens from being created to begin with by specifying the JsonSerializer with its NullValueHandler set to NullValueHandler.Ignore. This is passed in as a parameter to JObject.FromObject as seen in an answer to the same question you linked to: https://stackoverflow.com/a/29259032/263139.
Brian's answer works. I also came up with another (yet still recursive) way of doing it shortly after posting the question, in case anyone else is interested.
private void RemoveNullNodes(JToken root)
{
if (root is JValue)
{
if (((JValue)root).Value == null)
{
((JValue)root).Parent.Remove();
}
}
else if (root is JArray)
{
((JArray)root).ToList().ForEach(n => RemoveNullNodes(n));
if (!(((JArray)root)).HasValues)
{
root.Parent.Remove();
}
}
else if (root is JProperty)
{
RemoveNullNodes(((JProperty)root).Value);
}
else
{
var children = ((JObject)root).Properties().ToList();
children.ForEach(n => RemoveNullNodes(n));
if (!((JObject)root).HasValues)
{
if (((JObject)root).Parent is JArray)
{
((JArray)root.Parent).Where(x => !x.HasValues).ToList().ForEach(n => n.Remove());
}
else
{
var propertyParent = ((JObject)root).Parent;
while (!(propertyParent is JProperty))
{
propertyParent = propertyParent.Parent;
}
propertyParent.Remove();
}
}
}
}
Using JsonPath we can have a more elegant solution:
jObject.SelectTokens("$..*")
.OfType<JValue>()
.Where(x=>x.Type == JTokenType.Null)
.Select(a => a.Parent)
.ToList()
.ForEach(a => a.Remove());
With a working example here: https://dotnetfiddle.net/zVgXOq
Here's what I was able to come up with. It removes properties that contain only null values. This means that it will handle the case where the property is a scalar value that is null and will also handle the case where there is an array that is all null values. It also removes properties that have no values. This handles the case where the property contains an object that has no child properties. Note, mine uses a JObject which has a Descendents() method which is what made the implementation easy. JToken doesn't have that. My implementation mutates the JObject itself rather than creating a copy of it. Also, it continues removing properties until there aren't any more occurrences. It's a bit more succinct than the other implementations. I don't know how it compares performance-wise.
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Linq;
namespace JsonConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var jo = JObject.Parse(File.ReadAllText(#"test.json"));
Console.WriteLine($"BEFORE:\r\n{jo}");
jo.RemoveNullAndEmptyProperties();
Console.WriteLine($"AFTER:\r\n{jo}");
}
}
public static class JObjectExtensions
{
public static JObject RemoveNullAndEmptyProperties(this JObject jObject)
{
while (jObject.Descendants().Any(jt => jt.Type == JTokenType.Property && (jt.Values().All(a => a.Type == JTokenType.Null) || !jt.Values().Any())))
foreach (var jt in jObject.Descendants().Where(jt => jt.Type == JTokenType.Property && (jt.Values().All(a => a.Type == JTokenType.Null) || !jt.Values().Any())).ToArray())
jt.Remove();
return jObject;
}
}
}
The following is the program output:
BEFORE:
{
"propertyWithValue": "",
"propertyWithObjectWithProperties": {
"nestedPropertyWithValue": "",
"nestedPropertyWithNull": null
},
"propertyWithEmptyObject": {},
"propertyWithObjectWithPropertyWithNull": {
"nestedPropertyWithNull": null
},
"propertyWithNull": null,
"emptyArray": [],
"arrayWithNulls": [
null,
null
],
"arrayWithObjects": [
{
"propertyWithValue": ""
},
{
"propertyWithNull": null
}
]
}
AFTER:
{
"propertyWithValue": "",
"propertyWithObjectWithProperties": {
"nestedPropertyWithValue": ""
},
"arrayWithObjects": [
{
"propertyWithValue": ""
},
{}
]
}
Related
I have an arbitrary JSON document (i.e. without a fixed schema that is known in advance) and I would like to recursively descend it to search for all nodes at any level in the document that match some predicate, so that I can make some necessary modifications. How can I perform such a recursive search using the JsonNode document object model?
Specifics are as follows.
Say I have some JSON such as the following that may contain one or more instances of a property "password" inside:
[
{
"column1": "val_column1",
"column2": "val_column2",
"sheet2": [
{
"sheet2col1": "val_sheet2column1",
"sheet3": [
{
"sheet3col1": "val_sheet3column1",
"password": "password to remove"
}
]
},
{
"sheet2col1": "val_sheet2column1",
"sheet3": [
{
"sheet3col1": "val_sheet3column1"
}
]
}
]
},
{
"column1": "val2_column1",
"column2": "val2_column2",
"password": "password to remove",
"sheet2": [
{
"sheet2col1": "val_sheet2column1",
"sheet3": [
{
"sheet3col2": "val_sheet3column2"
},
null,
null,
19191
],
"password": "password to remove"
},
{
"sheet2col1": "val_sheet2column1",
"sheet3": [
{
"sheet3col2": "val_sheet3column2"
}
]
}
]
}
]
I need to parse it to a JsonNode hierarchy and remove all of the "password" properties wherever they may appear in the JSON hierarchy. With Json.NET, I could parse to JToken and use DescendantsAndSelf():
var root = JToken.Parse(json);
var propertyToRemove = "password";
if (root is JContainer c)
foreach (var obj in c.DescendantsAndSelf().OfType<JObject>().Where(o => o.ContainsKey(propertyToRemove)))
obj.Remove(propertyToRemove);
var newJson = root.ToString();
But JsonNode does not have an equivalent method. How can I do this using System.Text.Json?
Since JsonNode does not have an equivalent to DescendantsAndSelf() we will have to create one ourselves:
public static partial class JsonExtensions
{
public static IEnumerable<JsonNode?> Descendants(this JsonNode? root) => root.DescendantsAndSelf(false);
/// Recursively enumerates all JsonNodes in the given JsonNode object in document order.
public static IEnumerable<JsonNode?> DescendantsAndSelf(this JsonNode? root, bool includeSelf = true) =>
RecursiveEnumerableExtensions.Traverse(
root,
(n) => n switch
{
JsonObject o => o.AsDictionary().Values,
JsonArray a => a,
_ => n.ToEmptyEnumerable(),
}, includeSelf);
/// Recursively enumerates all JsonNodes (including their index or name and parent) in the given JsonNode object in document order.
public static IEnumerable<(JsonNode? node, int? index, string? name, JsonNode? parent)> DescendantItemsAndSelf(this JsonNode? root, bool includeSelf = true) =>
RecursiveEnumerableExtensions.Traverse(
(node: root, index: (int?)null, name: (string?)null, parent: (JsonNode?)null),
(i) => i.node switch
{
JsonObject o => o.AsDictionary().Select(p => (p.Value, (int?)null, p.Key.AsNullableReference(), i.node.AsNullableReference())),
JsonArray a => a.Select((item, index) => (item, index.AsNullableValue(), (string?)null, i.node.AsNullableReference())),
_ => i.ToEmptyEnumerable(),
}, includeSelf);
static IEnumerable<T> ToEmptyEnumerable<T>(this T item) => Enumerable.Empty<T>();
static T? AsNullableReference<T>(this T item) where T : class => item;
static Nullable<T> AsNullableValue<T>(this T item) where T : struct => item;
static IDictionary<string, JsonNode?> AsDictionary(this JsonObject o) => o;
}
public static partial class RecursiveEnumerableExtensions
{
// Rewritten from the answer by Eric Lippert https://stackoverflow.com/users/88656/eric-lippert
// to "Efficient graph traversal with LINQ - eliminating recursion" http://stackoverflow.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion
// to ensure items are returned in the order they are encountered.
public static IEnumerable<T> Traverse<T>(
T root,
Func<T, IEnumerable<T>> children, bool includeSelf = true)
{
if (includeSelf)
yield return root;
var stack = new Stack<IEnumerator<T>>();
try
{
stack.Push(children(root).GetEnumerator());
while (stack.Count != 0)
{
var enumerator = stack.Peek();
if (!enumerator.MoveNext())
{
stack.Pop();
enumerator.Dispose();
}
else
{
yield return enumerator.Current;
stack.Push(children(enumerator.Current).GetEnumerator());
}
}
}
finally
{
foreach (var enumerator in stack)
enumerator.Dispose();
}
}
}
And now we will be able to do:
var root = JsonNode.Parse(json);
var propertyToRemove = "password";
foreach (var obj in root.DescendantsAndSelf().OfType<JsonObject>().Where(o => o.ContainsKey(propertyToRemove)))
obj.Remove(propertyToRemove);
var options = new JsonSerializerOptions { WriteIndented = true /* Use whatever you want here */ };
var newJson = JsonSerializer.Serialize(root, options);
Demo fiddle #1 here.
Keep in mind the following differences with Json.NET's LINQ to JSON:
The JsonNode returned for a null JSON value (e.g. {"value":null}) will actually be null. LINQ to JSON represents a null JSON value as a non-null JValue with JValue.Type equal to JTokenType.Null.
JsonNode does not have any equivalent to Json.NET's JProperty. The parent of a value in an object will be the object itself. Thus there's no straightforward way to determine the property name of a selected JsonNode property value via the JsonNode document object model.
Thus if you need to search for and modify properties by value (rather than by name), you can use the second extension method DescendantItemsAndSelf() which includes the parent and name or index along with the current node. E.g., to remove all null property values, do the following:
foreach (var item in root.DescendantItemsAndSelf().Where(i => i.name != null && i.node == null).ToList())
((JsonObject)item.parent!).Remove(item.name!);
Demo fiddle #2 here.
I am working with System.Text.Json in my project as I am processing large files so also decided to use it for processing GraphQL responses.
Due to the nature of GraphQL sometimes I get highly nested responses that are not fixed and don't make sense to map to a class. I usually need to check a few properties on the response.
My issue is with JsonElement. To check nested properties feels very clumsy and I feel like there should be a better way to approach this.
For example take my below code simulating a response I get. I just want to check if 2 properties exist (id & originalSrc) and if they do get their value but it feels like I have made a meal of the code. Is there a better/clearer/more succinct way to write this?
var raw = #"{
""data"": {
""products"": {
""edges"": [
{
""node"": {
""id"": ""gid://shopify/Product/4534543543316"",
""featuredImage"": {
""originalSrc"": ""https://cdn.shopify.com/s/files/1/0286/pic.jpg"",
""id"": ""gid://shopify/ProductImage/146345345339732""
}
}
}
]
}
}
}";
var doc = JsonSerializer.Deserialize<JsonElement>(raw);
JsonElement node = new JsonElement();
string productIdString = null;
if (doc.TryGetProperty("data", out var data))
if (data.TryGetProperty("products", out var products))
if (products.TryGetProperty("edges", out var edges))
if (edges.EnumerateArray().FirstOrDefault().ValueKind != JsonValueKind.Undefined && edges.EnumerateArray().First().TryGetProperty("node", out node))
if (node.TryGetProperty("id", out var productId))
productIdString = productId.GetString();
string originalSrcString = null;
if(node.ValueKind != JsonValueKind.Undefined && node.TryGetProperty("featuredImage", out var featuredImage))
if (featuredImage.TryGetProperty("originalSrc", out var originalSrc))
originalSrcString = originalSrc.GetString();
if (!string.IsNullOrEmpty(productIdString))
{
//do stuff
}
if (!string.IsNullOrEmpty(originalSrcString))
{
//do stuff
}
It is not a crazy amount of code but checking a handful of properties is so common I would like a cleaner more readble approach.
You could add a couple of extension methods that access a child JsonElement value by property name or array index, returning a nullable value if not found:
public static partial class JsonExtensions
{
public static JsonElement? Get(this JsonElement element, string name) =>
element.ValueKind != JsonValueKind.Null && element.ValueKind != JsonValueKind.Undefined && element.TryGetProperty(name, out var value)
? value : (JsonElement?)null;
public static JsonElement? Get(this JsonElement element, int index)
{
if (element.ValueKind == JsonValueKind.Null || element.ValueKind == JsonValueKind.Undefined)
return null;
// Throw if index < 0
return index < element.GetArrayLength() ? element[index] : null;
}
}
Now calls to access nested values can be chained together using the null-conditional operator ?.:
var doc = JsonSerializer.Deserialize<JsonElement>(raw);
var node = doc.Get("data")?.Get("products")?.Get("edges")?.Get(0)?.Get("node");
var productIdString = node?.Get("id")?.GetString();
var originalSrcString = node?.Get("featuredImage")?.Get("originalSrc")?.GetString();
Int64? someIntegerValue = node?.Get("Size")?.GetInt64(); // You could use "var" here also, I used Int64? to make the inferred type explicit.
Notes:
The extension methods above will throw an exception if the incoming element is not of the expected type (object or array or null/missing). You could loosen the checks on ValueKind if you never want an exception on an unexpected value type.
There is an open API enhancement request Add JsonPath support to JsonDocument/JsonElement #31068. Querying via JSONPath, if implemented, would make this sort of thing easier.
If you are porting code from Newtonsoft, be aware that JObject returns null for a missing property, while JArray throws on an index out of bounds. Thus you might want to use the JElement array indexer directly when trying to emulate Newtonsoft's behavior, like so, since it also throws on an index out of bounds:
var node = doc.Get("data")?.Get("products")?.Get("edges")?[0].Get("node");
Demo fiddle here.
To make my code a little more readable I created a method that uses a dot-separated path with System.Text.Json similar to a path parameter for the SelectToken() method in Newtonsoft.Json.
JsonElement jsonElement = GetJsonElement(doc, "data.products.edges");
I then use jsonElement.ValueKind to check the return type.
private static JsonElement GetJsonElement(JsonElement jsonElement, string path)
{
if (jsonElement.ValueKind == JsonValueKind.Null ||
jsonElement.ValueKind == JsonValueKind.Undefined)
{
return default;
}
string[] segments =
path.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
for (int n = 0; n < segments.Length; n++)
{
jsonElement = jsonElement.TryGetProperty(segments[n], out JsonElement value) ? value : default;
if (jsonElement.ValueKind == JsonValueKind.Null ||
jsonElement.ValueKind == JsonValueKind.Undefined)
{
return default;
}
}
return jsonElement;
}
I created another simple method to retrieve the value of the returned JsonElement as a string.
private static string GetJsonElementValue(JsonElement jsonElement)
{
return
jsonElement.ValueKind != JsonValueKind.Null &&
jsonElement.ValueKind != JsonValueKind.Undefined ?
jsonElement.ToString() :
default;
}
Below are two functions applied to the OP's sample:
public void Test()
{
string raw = #"{
""data"": {
""products"": {
""edges"": [
{
""node"": {
""id"": ""gid://shopify/Product/4534543543316"",
""featuredImage"": {
""originalSrc"": ""https://cdn.shopify.com/s/files/1/0286/pic.jpg"",
""id"": ""gid://shopify/ProductImage/146345345339732""
}
}
}
]
}
}
}";
JsonElement doc = JsonSerializer.Deserialize<JsonElement>(raw);
JsonElement jsonElementEdges = GetJsonElement(doc, "data.products.edges");
string originalSrcString = default;
string originalIdString = default;
if (jsonElementEdges.ValueKind == JsonValueKind.Array)
{
int index = 0; // Get the first element in the 'edges' array
JsonElement edgesFirstElem =
jsonElementEdges.EnumerateArray().ElementAtOrDefault(index);
JsonElement jsonElement =
GetJsonElement(edgesFirstElem, "node.featuredImage.originalSrc");
originalSrcString = GetJsonElementValue(jsonElement);
jsonElement =
GetJsonElement(edgesFirstElem, "node.featuredImage.id");
originalIdString = GetJsonElementValue(jsonElement);
}
if (!string.IsNullOrEmpty(originalSrcString))
{
// do stuff
}
if (!string.IsNullOrEmpty(originalIdString))
{
// do stuff
}
}
I have developed a small library named JsonEasyNavigation, you can get it on github or from nuget.org. It allows you to navigate through JSON Domain Object Model using indexer-like syntax:
var jsonDocument = JsonDocument.Parse(json);
var nav = jsonDocument.ToNavigation();
ToNavigation() method converts JsonDocument into readonly struct named JsonNavigationElement. It has property and array item indexers, for example:
var item = nav["data"]["product"]["edges"][0];
Then you can check for actual items existince like this:
if (item.Exist)
{
var id = item["id"].GetStringOrEmpty();
// ...
}
I hope you will find it useful.
Thank Dave B for a good idea. I have improved it to be more efficient when accessing array elements without having to write too much code.
string raw = #"{
""data"": {
""products"": {
""edges"": [
{
""node"": {
""id"": ""gid://shopify/Product/4534543543316"",
""featuredImage"": {
""originalSrc"": ""https://cdn.shopify.com/s/files/1/0286/pic.jpg"",
""id"": ""gid://shopify/ProductImage/146345345339732""
}
}
},
{
""node"": {
""id"": ""gid://shopify/Product/123456789"",
""featuredImage"": {
""originalSrc"": ""https://cdn.shopify.com/s/files/1/0286/pic.jpg"",
""id"": [
""gid://shopify/ProductImage/123456789"",
""gid://shopify/ProductImage/666666666""
]
},
""1"": {
""name"": ""Tuanh""
}
}
}
]
}
}
}";
Usage is also quite simple
JsonElement doc = JsonSerializer.Deserialize<JsonElement>(raw);
JsonElement jsonElementEdges = doc.GetJsonElement("data.products.edges.1.node.1.name");
public static JsonElement GetJsonElement(this JsonElement jsonElement, string path)
{
if (jsonElement.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
return default;
string[] segments = path.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
foreach (var segment in segments)
{
if (int.TryParse(segment, out var index) && jsonElement.ValueKind == JsonValueKind.Array)
{
jsonElement = jsonElement.EnumerateArray().ElementAtOrDefault(index);
if (jsonElement.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
return default;
continue;
}
jsonElement = jsonElement.TryGetProperty(segment, out var value) ? value : default;
if (jsonElement.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
return default;
}
return jsonElement;
}
public static string? GetJsonElementValue(this JsonElement jsonElement) => jsonElement.ValueKind != JsonValueKind.Null &&
jsonElement.ValueKind != JsonValueKind.Undefined
? jsonElement.ToString()
: default;
Depending on the type of JsonElement returned you have to handle it differently.
My case was that the returned element was ValueKind = Array : "[[47.751]]"
So in order to get it I did created this method
private object GetValueFromJsonElement(WorkbookRange range)
{
// The RootElement is the JsonElement
var element = range.Values.RootElement.EnumerateArray().First()[0];
switch (element.ValueKind)
{
case JsonValueKind.Number:
return element.GetDouble();
case JsonValueKind.String:
return element.GetString();
case JsonValueKind.True:
case JsonValueKind.False:
return element.GetBoolean();
default:
throw new InvalidOperationException("The Value Type returned is not handled");
}
}Depending on the type of JsonElement returned you have to handle it differently.
The business rules are simple. We have a method that takes a JObject as a parm. Convert it to a c# poco.
The json needs to represent a single object.
No arrays allowed. If you need to do it three times call the method three times.
So for example this would be valid json:
{
"CustomerId": 669616948,
"FirstName": "ERIC",
"LastName": "TEST2",
"BirthYear": 0,
"BirthMonth": 0,
"CustomerState": 0,
"LegalAddressState": null,
"Username": "ERIC2222"
}
this would not:
{
"Participants": [
{
"CustomerId": 669616948,
"FirstName": "ERIC",
"LastName": "TEST2",
"BirthYear": 0,
"BirthMonth": 0,
"CustomerState": 0,
"LegalAddressState": null,
"Username": "ERIC2222"
}
]
}
Currently this throws an exception when it tries to convert to a poco and while we can handle the exception I was looking for a way to detect if the JObject contained an array and gracefully exit.
So the json above is just a representation of what the JObject would look like but it IS a JObject.
The best I have been able to come up with is a measly string check.
JObject.ToString().Contains("[")
Any ideas on how to do an array check. If I could somehow get it to a JToken then I could do this (temp is of type JToken):
temp.Type == JTokenType.Array
TIA
As requested here is the conversion. payload is a JObject.
var customer = payload.ToObject<Customer>(_serializer);
What about this way?
dynamic value = jToken["Participants"];
if (value != null && value is JArray)
{
//gracefully exit.
}
You can always write a custom JsonConverter that walks the json tree (using a technique such as that described in this answer https://stackoverflow.com/a/19646950/1165998), checking both the type and the value type for JArray and returning null if it is:
public class ProhibitArraysConverter<T> : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
var jsonObject = JToken.Load(reader);
if (ContainsArray(jsonObject))
return null;
T target = (T)Activator.CreateInstance(objectType);
serializer.Populate(jsonObject.CreateReader(), target);
return target;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
private static bool ContainsArray(JToken containerToken)
{
if (containerToken.Type == JTokenType.Object)
{
foreach (JProperty child in containerToken.Children<JProperty>())
{
if (child.Type == JTokenType.Array ||
child.Value.Type == JTokenType.Array)
{
return true;
}
ContainsArray(child.Value);
}
}
else if (containerToken.Type == JTokenType.Array)
{
return true;
}
return false;
}
}
This will return the deserialized data in your first example and null for your second.
Am not quite sure about your requirements. But this is one way to go:
[TestMethod]
public void DeserializeTest()
{
var jsonStr1 = "{\"CustomerId\": 669616948,\"FirstName\": \"ERIC\",\"LastName\": \"TEST2\",\"BirthYear\": 0,\"BirthMonth\": 0,\"CustomerState\": 0,\"LegalAddressState\": null,\"Username\": \"ERIC2222\"}";
JToken token1 = JToken.Parse(jsonStr1);
var participantsFromToken1 = token1["Participants"];
Console.WriteLine(participantsFromToken1 != null && participantsFromToken1.Type == JTokenType.Array
? "Hey, token1 is an array"
: "Hey, token1 is not an array");
var jsonStr2 =
"{\"Participants\": [{\"CustomerId\": 669616948,\"FirstName\": \"ERIC\",\"LastName\": \"TEST2\",\"BirthYear\": 0,\"BirthMonth\": 0,\"CustomerState\": 0,\"LegalAddressState\": null,\"Username\": \"ERIC2222\"}]}";
JToken token2 = JToken.Parse(jsonStr2);
var participantsFromToken2 = token2["Participants"];
Console.WriteLine(participantsFromToken2 != null && participantsFromToken2.Type == JTokenType.Array
? "Hey, token2 is an array"
: "Hey, token2 is not an array");
}
Suppose I have the following JToken:
#"{
""data"": [
{
""company"": {
""ID"": ""12345"",
""location"": ""Some Location""
},
""name"": ""Some Name""
}
]
}";
I want to pass this token into a FlattenToken function that outputs this JToken:
#"{
""data"": [
{
""company_ID"": ""12345"",
""company_location"": ""Some Location"",
""name"": ""Some Name""
}
]}"
The reason for doing this is so that I can then take the flattened JToken and deserialize it into a DataTable.
I'm getting lost in a jumble of JObjects, JTokens, JProperties, and other JMadness, though. I saw the answer on this post, which was helpful, but I'm still not getting it right.
Here's what I have so far:
public static JToken FlattenToken(JToken token)
{
foreach (JToken topLevelItem in token["data"].Children())
{
foreach (JToken field in topLevelItem.Value<JToken>())
{
foreach (JProperty property in field.Value<JObject>().Properties())
{
field.AddAfterSelf(JObject.Parse(#"{""" + property.Name + "_" + property.Value));
}
field.Remove();
}
}
return token;
}
The first iteration through the outer foreach loop, topLevelItem =
{
"company": {
"ID": "12345"
},
"name": "Some Name"
}
And the first iteration through the second foreach loop, field =
"company": {
"ID": "12345"
}
Looking good so far. But when I hit the innermost foreach loop, I get an exception on the foreach line: "Cannot cast Newtonsoft.Json.Linq.JProperty to Newtonsoft.Json.Linq.JToken."
Not sure what's going on there. I was under the impression that the field.Value call was going to produce a JToken and try to cast it to a JProperty. So where is a JProperty trying to be casted to a JToken, as the error suggests?
Also, this feels like a pretty gross way of flattening out a JToken. Is there a better way?
The hierarchy of objects in Json.NET can be rather deep. A rough guide can be found in this answer.
To solve your problem, you first need an extension method to take the properties of a JObject and return then in a collection with a name prefix:
public static class JsonExtensions
{
public static IEnumerable<KeyValuePair<string, JToken>> FlattenFields(this JObject obj, string prefix)
{
foreach (var field in obj)
{
string fieldName = prefix + "_" + field.Key;
var fieldValue = field.Value;
yield return new KeyValuePair<string, JToken>(fieldName, fieldValue);
}
}
}
Next, you need some recursive tools to iterate through a Json.NET hierarchy and rewrite the collection of properties of selected JObject's:
public static class JsonExtensions
{
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
public static JToken EditFields(this JToken token, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor)
{
if (token == null)
return null;
switch (token.Type)
{
case JTokenType.Array:
return EditFields((JArray)token, editor);
case JTokenType.Object:
return EditFields((JObject)token, editor);
default:
return token;
}
}
static JToken EditFields(JArray array, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor)
{
JArray newArray = null;
foreach (var element in array)
{
var newElement = EditFields(element, editor);
if (newElement != null)
{
if (newArray == null)
newArray = new JArray();
newArray.Add(newElement);
}
}
return newArray;
}
static JToken EditFields(JObject obj, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor)
{
JObject newObj = null;
foreach (var field in obj)
{
foreach (var newField in editor(field))
{
if (newObj == null)
newObj = new JObject();
newObj[newField.Key] = newField.Value.EditFields(editor);
}
}
return newObj;
}
}
Finally, put these together to create a method that promotes properties of a named JObject property to their parent JObject, prepending the property name plus an underscore:
public static class JsonExtensions
{
public static JToken PromoteNamedPropertiesToParents(this JToken token, string propertyName)
{
return token.EditFields(pair =>
{
if (pair.Key == propertyName && pair.Value is JObject)
{
return ((JObject)pair.Value).FlattenFields(pair.Key);
}
return pair.Yield();
});
}
}
And then, to test:
public static class TestFlatten
{
public static void Test()
{
string jsonString = #"{
""data"": [
{
""company"": {
""ID"": ""12345"",
""location"": ""Some Location""
},
""name"": ""Some Name""
}
]
}";
JObject obj = JObject.Parse(jsonString);
var newObj = (JObject)obj.PromoteNamedPropertiesToParents("company");
Debug.WriteLine(newObj);
}
}
And the output is:
{
"data": [
{
"company_ID": "12345",
"company_location": "Some Location",
"name": "Some Name"
}
]
}
Which is what you want. Please note that this code creates a new JObject hierarchy rather than modifying the original hierarchy.
I am trying to parse a json file using json.net. The file looks like this
{X:
{
Title:"foo",
xxxx:xxxx
}
}
{Y:
{ZZ:
{Title: "bar",...}
}
}
I am trying to recurse down this structure processing all objects with a Title attribute. But I am confused about JToken, JProperty, JContainer, JValue, JObject. Reading the source code has not left me much wiser and none of the samples help. I want something along the lines of
WalkNode(node, Action<Node> action)
{
foreach(var child in node.Children)
{
Action(child);
WalkNode(child);
}
}
Parse()
{
WalkNode(root, n=>
{
if(n["Title"] != null)
{
...
}
});
}
The code below should be pretty close to what you are looking for. I made the assumption that there is an outer array, and that arrays can appear anywhere in the hierarchy. (If this is not true, you can simplify the WalkNode method code a bit, but it should work either way.)
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonRecursiveDescent
{
class Program
{
static void Main(string[] args)
{
string json =
#"[
{
""X"":
{
""Title"":""foo"",
""xxxx"":""xxxx""
}
},
{
""Y"":
{
""ZZ"":
{
""Title"":""bar"",
""xxxx"":""xxxx""
}
}
}
]";
JToken node = JToken.Parse(json);
WalkNode(node, n =>
{
JToken token = n["Title"];
if (token != null && token.Type == JTokenType.String)
{
string title = token.Value<string>();
Console.WriteLine(title);
}
});
}
static void WalkNode(JToken node, Action<JObject> action)
{
if (node.Type == JTokenType.Object)
{
action((JObject)node);
foreach (JProperty child in node.Children<JProperty>())
{
WalkNode(child.Value, action);
}
}
else if (node.Type == JTokenType.Array)
{
foreach (JToken child in node.Children())
{
WalkNode(child, action);
}
}
}
}
}
Also needed to do something of the sorts.
Would like to propose my solution. It has the advantage of:
not being recursive
no callbacks
not assuming any internal structure (arrays)
decouples tree traversal from the action needed to be executed
IEnumerable<JToken> AllTokens(JObject obj) {
var toSearch = new Stack<JToken>(obj.Children());
while (toSearch.Count > 0) {
var inspected = toSearch.Pop();
yield return inspected;
foreach (var child in inspected) {
toSearch.Push(child);
}
}
}
Then you can use linq to filter and perform action:
var tokens = AllTokens(jsonObj);
var titles = tokens.Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == "Title");
I thought I'd include my minor tweaks to #BrianRogers WalkNode method, made it slightly more versatile:
private static void WalkNode(JToken node,
Action<JObject> objectAction = null,
Action<JProperty> propertyAction = null)
{
if (node.Type == JTokenType.Object)
{
if (objectAction != null) objectAction((JObject) node);
foreach (JProperty child in node.Children<JProperty>())
{
if (propertyAction != null) propertyAction(child);
WalkNode(child.Value, objectAction, propertyAction);
}
}
else if (node.Type == JTokenType.Array)
{
foreach (JToken child in node.Children())
{
WalkNode(child, objectAction, propertyAction);
}
}
}
Then OP could do something like:
WalkNode(json, null, prop =>
{
if (prop.Name == "Title" && prop.Value.Type == JTokenType.String)
{
string title = prop.Value<string>();
Console.WriteLine(title);
}
});
You could also do it with JSONPath: node.SelectTokens("$..*");
Used like this:
var jObjectsWithTitle = node
.SelectTokens("$..*")
.OfType<JObject>()
.Where(x => x.Property("Title") != null);
Or just:
var jObjectsWithTitle = node.SelectTokens("$..[?(#.Title)]");
Try this Method I have written it after some unsuccessful tries:
private void Traverse(JToken token, TreeNode tn)
{
if (token is JProperty)
if (token.First is JValue)
tn.Nodes.Add(((JProperty)token).Name + ": " + ((JProperty)token).Value);
else
tn = tn.Nodes.Add(((JProperty)token).Name);
foreach (JToken token2 in token.Children())
Traverse(token2, tn);
}
You first have to pass it the complete JSON file like this:
TreeNode rooty= tvu.Nodes.Add("Rooty") // not the Indian bread,just Rooty, Ok?
JToken token = JToken.Parse(File.ReadAllText(<"Path to json file">));
Traverse(token, rooty);
Done, Boom you got this one:
Oh no, I am not allowed to embed pictures. sad.
I wrapped the data with a "root" node, and wrapped it all as an array value. Then this works for me:
private static void TraverseJson(string jsonText)
{
dynamic jsonObject = JsonConvert.DeserializeObject(jsonText);
JArray ja = (JArray)jsonObject.root;
int itemCount = ja.Count;
foreach (JObject jobj in jsonObject.root)
{
WalkHierarchy(jobj);
}
}
private static void WalkHierarchy(JObject jobj)
{
foreach (JProperty jprop in (JToken)jobj)
{
if (jprop.Value.Type == JTokenType.Object)
{
WalkHierarchy((JObject)jprop.Value);
}
else
{
Console.WriteLine(jprop.Value.ToString());
}
}
}