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

查看:350
本文介绍了如何在生成的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": {}
    }
}

如果您设法使用Regex删除了最深层的空对象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属性,然后传递来实现.如下所示. (必须这样做,因为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天全站免登陆