一尘不染

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

json

我有一个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类型,而不是真正的anull,这很有意义。但是,在这种非常特殊的情况下,我需要能够摆脱这些“未使用”的字段。我尝试过手动遍历节点并删除它们,但这也不起作用。注意,我使用的唯一托管类型是JObject;
我没有将对象转换为对象或在其上定义属性的模型,因为此“模板”在运行时已解析。我只是想知道是否有人遇到过这样的问题并且有任何见解。任何帮助是极大的赞赏!


阅读 293

收藏
2020-07-27

共1个答案

一尘不染

您可以使用一种类似于下面的递归帮助器方法来在序列化之前nullJToken层次结构中删除值。

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

演示:

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

2020-07-27