JSON.NET在忽略null属性的同时序列化JObject

我有一个JObject ,用作调用RESTful Web服务的模板 。 这个JObject是通过解析器创建的,因为它用作模板告诉用户端点模式是什么样的,我不得不想办法保留所有属性,这就是为什么我将它们的值默认为null 。 例如,这就是对象最初的样子:

 { "Foo":{ "P1":null, "P2":null, "P3":null, "P4":{ "P1":null, "P2":null, "P3":null, }, "FooArray":[ { "F1":null, "F2":null, "F3":null, } ] }, "Bar":null } 

然后,用户可以根据需要填写单个字段,例如Foo.P2Foo.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 } 

意思是他们只关心这两个领域。 现在我想将此模板( JObject )序列化为JSON字符串,但只想要填充的那些字段显示出来。 所以我尝试了这个:

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

不幸的是,这不起作用。 我遇到了这个问题,并意识到对象中的null值是一个实际的JToken类型而不是真正的null ,这是有道理的。 但是,在这种非常特殊的情况下,我需要能够摆脱这些“未使用”的字段。 我尝试手动迭代节点并删除它们,但这也不起作用。 请注意,我使用的唯一托管类型是JObject ; 我没有将对象转换为或定义属性的模型,因为此“模板”在运行时得到解析。 我只是想知道是否有人遇到过这样的问题并且有任何见解。 任何帮助是极大的赞赏!

您可以使用类似下面的递归帮助方法在序列化之前从JToken层次结构中删除null值。

 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()) { 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); } } 

演示:

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

输出:

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

小提琴: https : //dotnetfiddle.net/wzEOie

请注意,在删除所有空值后,您将在FooArray有一个空对象,您可能不需要它。 (如果该对象被删除,那么你将有一个空的FooArray ,你也可能不想要它。)如果你想让helper方法在删除时更具攻击性,你可以将IsEmpty函数更改为:

  public static bool IsEmpty(JToken token) { return (token.Type == JTokenType.Null) || (token.Type == JTokenType.Array && !token.HasValues) || (token.Type == JTokenType.Object && !token.HasValues); } 

有了这个改变,你的输出将会是这样的:

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

小提琴: https : //dotnetfiddle.net/ZdYogJ

您可以通过将NullValueHandler设置为NullValueHandler.Ignore来指定JsonSerializer来阻止创建空标记。 这将作为参数传递给JObject.FromObject如您链接到的相同问题的答案中所示: https : JObject.FromObject

Brian的回答有效。 在发布问题后不久我也提出了另一种(但仍然是递归的)方式,以防其他人感兴趣。

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