我可以在运行时选择性地关闭JsonIgnore属性吗?

35
我正在使用Newtonsoft.Json从一组类创建JSON文件。由于生成的文件非常大,因此我已经为属性创建了JsonProperty以减小文件大小,并添加了JsonIgnore和某些数据类型的自定义格式化。
结果将文件从24MB减小到1MB,这很好; 但是,我希望在运行时有选项来生成完整版本或简化属性版本。
是否有任何方法可以使序列化程序可选地使用这些属性?
3个回答

72

是的,可以使用自定义的ContractResolver来实现这个功能。

由于您没有展示任何代码,所以我来举个例子。假设我有一个类Foo如下所示。我希望在序列化输出中包含IdName属性,但我绝对不会关心AlternateNameColor,因此我使用了[JsonIgnore]标记它们。我希望描述信息出现,但有时它可能会很长,因此我使用了自定义的JsonConverter来限制其长度。我还想为描述信息使用较短的属性名称,因此我用[JsonProperty("Desc")]标记了它。

class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    [JsonIgnore]
    public string AlternateName { get; set; }
    [JsonProperty("Desc")]
    [JsonConverter(typeof(StringTruncatingConverter))]
    public string Description { get; set; }
    [JsonIgnore]
    public string Color { get; set; }
}

当我对上述实例进行序列化时...

Foo foo = new Foo
{
    Id = 1,
    Name = "Thing 1",
    AlternateName = "The First Thing",
    Description = "This is some lengthy text describing Thing 1 which you'll no doubt find very interesting and useful.",
    Color = "Yellow"
};

string json = JsonConvert.SerializeObject(foo, Formatting.Indented);

...我得到了这个输出:

{
  "Id": 1,
  "Name": "Thing 1",
  "Desc": "This is some lengthy text describing Thing 1 "
}

现在,假设有时我想要获取完整的JSON输出,而不考虑我的自定义设置。我可以使用自定义 ContractResolver 来以编程方式 "取消应用" 类中的属性。以下是解析器的代码:

class IgnoreJsonAttributesResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
        foreach (var prop in props)
        {
            prop.Ignored = false;   // Ignore [JsonIgnore]
            prop.Converter = null;  // Ignore [JsonConverter]
            prop.PropertyName = prop.UnderlyingName;  // restore original property name
        }
        return props;
    }
}
为了使用解析器,我将其添加到JsonSerializerSettings中,并像这样将设置传递给序列化程序:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new IgnoreJsonAttributesResolver();
settings.Formatting = Formatting.Indented;

string json = JsonConvert.SerializeObject(foo, settings);
输出现在包括被忽略的属性,并且说明不再被截断。
{
  "Id": 1,
  "Name": "Thing 1",
  "AlternateName": "The First Thing",
  "Description": "This is some lengthy text describing Thing 1 which you'll no doubt find very interesting and useful.",
  "Color": "Yellow"
}

完整演示在此处: https://dotnetfiddle.net/WZpeWt


太棒了,Brian的回答非常好用。你知道如何关闭[JsonProperty("ShortName")]属性吗? - Dan Craigen
3
在解析器的循环中加入 prop.PropertyName = prop.UnderlyingName;,这会使属性使用它的原始名称。 - Brian Rogers
@BrianRogers 很棒的回答。在我的场景中,我有一个对象图需要转换,对于树中的每个类型(在实现特定类型时),我需要根据它们的值包含/排除属性。 - Shimmy Weitzhandler
1
@Shimmy 如果您有后续问题,应该将其作为一个新问题提出。如果它有助于提供上下文,您可以包含指向此问题的链接。评论部分不是提问的好地方,因为评论在长度和格式上都受到限制;它们不可搜索,也不会出现在问题列表中,这意味着您很难得到令人满意的答案。 - Brian Rogers

-2

如果您愿意使用F#(或者只是使用一个不针对C#进行优化的API),FSharp.JsonSkippable库包含一个通用的包装类型,允许您以一种简单且强类型的方式控制在序列化时是否包括给定属性(并确定反序列化时是否包括属性),并且可以分别控制/确定可空性的排除。 (完全披露:我是该库的作者。)


-2

Json支持我们忽略不想返回的属性。 例如

class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public string AlternateName { get; set; }    
}

如何使用它:

Foo foo = new Foo
{
    Id = 1,
    Name = "Thing 1",
    AlternateName = null,   
};

string json = JsonConvert.SerializeObject(foo);

5
这并没有回答所问的问题。 - Brian Rogers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接