JSON.NET serializes JObject while ignoring null properties

I have a JObject that is used as a template to call RESTful web services. This JObject is created using the analyzer and, since it is used as a template to tell the user what the endpoint circuit looks like, I needed to figure out a way to save all the properties, so I don't execute my values ​​to null . As in the 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 can then fill in individual fields as needed, for example 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 } 

means that they care about all these two areas. Now I want to serialize this template ( JObject ) back to the JSON string, but I want only the fields that were filled to be displayed. So I tried this:

 string json = JsonConvert.SerializeObject(template, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); 

Unfortunately, this did not work. I came across this question and realized that the null value in the object is the actual JToken type and not really null , which makes sense. However, in this particular case, I need to get rid of these "unused" fields. I tried manually sorting the nodes and deleting them, but that didn't work either. Note that the only managed type that I use is JObject ; I do not have a model for transforming an object or defining attributes, since this "pattern" is allowed at runtime. I was just wondering if anyone ran into such a problem and she has any ideas. Any help is much appreciated!

+8
json c # nsjsonserialization
source share
3 answers

You can use a recursive helper method like the one below to remove null values ​​from the JToken hierarchy before it is serialized.

 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)); 

Exit:

 { "Foo": { "P2": "hello world", "P4": { "P1": 1 }, "FooArray": [ {} ] } } 

Fiddle: https://dotnetfiddle.net/wzEOie

Note that after deleting all the null values, you will have an empty object in FooArray , which you may not need. (And if this object was deleted, then you will have an empty FooArray , which you also might not need.) If you want the helper method to be more aggressive when deleting it, 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); } 

Given this change, your result will look like this:

 { "Foo": { "P2": "hello world", "P4": { "P1": 1 } } } 

Fiddle: https://dotnetfiddle.net/ZdYogJ

+12
source share

Brian answers. I also came up with another (but still recursive) way to do this 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(); } } } } 
+2
source share

You can prevent the creation of null tokens by specifying the JsonSerializer with its NullValueHandler set to NullValueHandler.Ignore . This is passed as a parameter to JObject.FromObject , as shown in the answer to the same question that you linked to: https://stackoverflow.com/a/316623/16 .

+2
source share

All Articles