如何在生成的JSON中省略/忽略/跳过空对象文字?

时间:2016-04-27 08:53:59

标签: c# json.net

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

例如:

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布尔方法,并检查所有属性是否都是默认属性。但是,我想以自定义合约解析器的形式提供一般解决方案。

3 个答案:

答案 0 :(得分:5)

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

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

如果你设法用Regex删除最深层次的空对象barbaz(同时还意识到你需要删除它们之间的逗号以保持JSON有效),你仍然会有剩下一个空对象:foo

{
    "foo":
    {
    }
}

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

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

}

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

var json = JsonHelper.SerializeToMinimalJson(obj);

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

修改

如果您想使用此方法来尊重[DefaultValue]属性,可以通过修改SerializeToMinimalJson()方法来创建JsonSerializer的实例来设置DefaultValueHandling。 }属性,然后将其传递给JToken.FromObject(),如下所示。 (必须以这种方式完成,因为JTokens没有引用回到使用FromObject()创建它们的原始对象,因此无法获取{的值之后的{1}}属性。)

[DefaultValue]

如果您这样做,您可能还想更改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(); } 方法,以便它不会删除&#34;默认默认值&#34;的值。你可以把它减少到这个:

IsEmptyOrDefault()

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

答案 1 :(得分:0)

您可以使用JsonSerializerSettings

为方法提供NullValueHandling.Ignore
var output = JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
    NullValueHandling = NullValueHandling.Ignore
});

如果此设置无法满足您的需求,请检查:the documentation.您可以找到所有属性和说明。

编辑:使用子(Sample)作为结构,它与DefaultValueHandling.Ignore一起使用。但由于类的复杂性,@ZoltánTamási将使用正则表达式。

答案 2 :(得分:0)

我实现了一个稍微不同的解决方案,它使用泛型方法,反射和一些默认的Newtonsoft.Json ShouldSerialize功能。不是优雅,但在概念上简单,我的特殊需要。以下是LinqPad代码片段。

void Main()
{
    Person person = new Person();
    person.MyAddress = new Address();
    var ret = person.ShouldSerializeMyAddress();

    var json = JsonConvert.SerializeObject(person, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings
    {
        NullValueHandling = NullValueHandling.Ignore
    });
    json.Dump();    
}

public static class JsonExtensions
{
    public static bool ShouldSerialize(this object self)
    {
        if (self == null)
            return false;

        var methods = self.GetType().GetMethods().Where(p => p.Name.StartsWith("ShouldSerialize"));
        return methods.Any(p => p.Invoke(self, null) is bool value && value);
    }
}

public class Person
{   
    public Address MyAddress { get; set; }  
    public bool ShouldSerializeMyAddress()
    {
        return MyAddress.ShouldSerialize();         
    }
}

public class Address
{
    public string Street { get; set; }
    public bool ShouldSerializeStreet()
    {
        return false;  // or whatever your property serialization criteria should be
    }
    public string City { get; set; }
    public bool ShouldSerializeCity()
    {
        return false;
    }
    public string State { get; set; }
    public bool ShouldSerializeState()
    {
        return false;
    }
    public string Zip { get; set; }
    public bool ShouldSerializeZip()
    {
        return false;
    }
}