如何在生成的 JSON 中省略/忽略/跳过空对象文字? [英] How to omit/ignore/skip empty object literals in the produced JSON?

查看:63
本文介绍了如何在生成的 JSON 中省略/忽略/跳过空对象文字?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Json.NET 将复杂的 C# 对象图转换为 JSON.由于忽略了对象中具有默认值的属性,我通常会在输出中得到空的对象文字,我想省略它.

I'm using Json.NET to convert a complex C# object graph to JSON. Due to ignoring properties which have default values in the object, I usually get empty object literals in the output, which I'd like to omit.

例如:

public class Sample {
  public int Value { get; set; }
  public string Name { get; set; }
}

public class ParentSample {
  // this property should never be null, hence the initializer
  public Sample Sample { get; } = new Sample();
}

..

var obj = new ParentSample();
// settings for indentation and excluding default values omitted for clarity
var output = JsonConvert.SerializeObject(obj, ... );
// output will be 
// {
//   Sample: {}
// }
//
// I'd like it to be 
// {}

我知道一些特定于类型的解决方案,例如将 ShouldSerializeSample 布尔方法添加到 ParentSample 类型并检查那里是否所有属性都是默认的.但是,我想要一个通用的解决方案,例如自定义合同解析器.

I'm aware of some type specific solutions like adding a ShouldSerializeSample boolean method to the ParentSample type and check if all properties are default there. However I'd like a general solution in the form of a custom contract resolver for example.

推荐答案

在评论中,您似乎决定使用 Regex 来摆脱空对象.这个想法的一个问题是它可能无法处理你拥有我称之为递归空对象"的情况.换句话说,是这样的:

In the comments it looks like you have decided to resort to using Regex to get rid of the empty objects. One problem with that idea is it probably will not handle the situation where you have what I will call "recursive empty objects". In other words something like this:

{
    "foo":
    {
        "bar": {},
        "baz": {}
    }
}

如果您设法使用正则表达式删除最深层的空对象 barbaz(同时还意识到您需要删除它们之间的逗号以保持 JSON 有效),您仍然会留下一个空对象:foo.

If you manage to remove the deepest level empty objects bar and baz with Regex (while also realizing that you need to remove the comma between them to keep the JSON valid), you will still have an empty object left: foo.

{
    "foo":
    {
    }
}

我认为更好的解决方案是将数据加载到 JToken 层次结构中,然后使用递归方法删除所有空子项,然后再将其写入 JSON.这样的东西应该可以满足您的需求:

I think a better solution is to load your data into a JToken hierarchy and then use a recursive method to remove all the empty children before writing it out to JSON. Something like this should work for your needs:

using System;
using Newtonsoft.Json.Linq;

public static class JsonHelper
{
    public static string SerializeToMinimalJson(object obj)
    {
        return JToken.FromObject(obj).RemoveEmptyChildren().ToString();
    }

    public static JToken RemoveEmptyChildren(this 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 = child.RemoveEmptyChildren();
                }
                if (!child.IsEmptyOrDefault())
                {
                    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 = child.RemoveEmptyChildren();
                }
                if (!child.IsEmptyOrDefault())
                {
                    copy.Add(child);
                }
            }
            return copy;
        }
        return token;
    }

    public static bool IsEmptyOrDefault(this JToken token)
    {
        return (token.Type == JTokenType.Array && !token.HasValues) ||
               (token.Type == JTokenType.Object && !token.HasValues) ||
               (token.Type == JTokenType.String && token.ToString() == String.Empty) ||
               (token.Type == JTokenType.Boolean && token.Value<bool>() == false) ||
               (token.Type == JTokenType.Integer && token.Value<int>() == 0) ||
               (token.Type == JTokenType.Float && token.Value<double>() == 0.0) || 
               (token.Type == JTokenType.Null);
    }

}

然后您可以像这样序列化您的对象:

You can then serialize your object(s) like this:

var json = JsonHelper.SerializeToMinimalJson(obj);

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

编辑

如果您想使用此方法尊重 [DefaultValue] 属性,您可以通过修改 SerializeToMinimalJson() 方法来创建 的实例>JsonSerializer,在其上设置DefaultValueHandling属性,然后将其传递给JToken.FromObject(),如下图.(必须这样做,因为 JTokens 没有对使用 FromObject() 创建它们的原始对象的引用,因此无法获取[DefaultValue] 属性的值.)

If you want to honor the [DefaultValue] attribute with this method, you can do so by modifying the SerializeToMinimalJson() method to create an instance of the JsonSerializer, setting the DefaultValueHandling property on it, and then passing it to JToken.FromObject() as shown below. (It has to be done this way because JTokens do not have references back to the original objects from which they were created using FromObject(), so there's no way to get the values of the [DefaultValue] attributes after that.)

public static string SerializeToMinimalJson(object obj)
{
    var serializer = new JsonSerializer();
    serializer.NullValueHandling = NullValueHandling.Ignore;
    serializer.DefaultValueHandling = DefaultValueHandling.Ignore;
    return JToken.FromObject(obj, serializer).RemoveEmptyChildren().ToString();
}

如果您这样做,您可能还需要更改 IsEmptyOrDefault() 方法,以便它不会删除默认默认值"的值.您可以将其简化为:

If you do that, you may also want to change the IsEmptyOrDefault() method so that it does not remove values that are the "default default". You can reduce it to this:

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

小提琴:https://dotnetfiddle.net/0yVRI5

这篇关于如何在生成的 JSON 中省略/忽略/跳过空对象文字?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆