使用 JSON.net 进行序列化时忽略接口定义的属性

15

我有一个接口,其中有一个像这样的属性:

public interface IFoo {
    // ...

    [JsonIgnore]
    string SecretProperty { get; }

    // ...
}

我希望在序列化所有实现类时忽略SecretProperty。但似乎我必须在每个实现属性上定义JsonIgnore属性。有没有一种方法可以在不必添加JsonIgnore属性到每个实现中的情况下达到这个目的?我没有找到任何有帮助的序列化设置。


如果这个解决方案能够解决你的问题,你应该将其添加为答案并接受它。 - vgru
我现在已经这样做了,但我之前不能这样做,因为我的问题发出后不到8小时是不允许这样做的(而且周末我也不在工作)。 - fero
4个回答

8

经过一些搜索,我找到了这个问题:

如何使用JSON.NET在将对象序列化时从接口继承属性到对象

我采用了Jeff Sternal的代码,并添加了JsonIgnoreAttribute检测,代码如下:

class InterfaceContractResolver : DefaultContractResolver
{
    public InterfaceContractResolver() : this(false) { }

    public InterfaceContractResolver(bool shareCache) : base(shareCache) { }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        var interfaces = member.DeclaringType.GetInterfaces();
        foreach (var @interface in interfaces)
        {
            foreach (var interfaceProperty in @interface.GetProperties())
            {
                // This is weak: among other things, an implementation 
                // may be deliberately hiding an interface member
                if (interfaceProperty.Name == member.Name && interfaceProperty.MemberType == member.MemberType)
                {
                    if (interfaceProperty.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Any())
                    {
                        property.Ignored = true;
                        return property;
                    }

                    if (interfaceProperty.GetCustomAttributes(typeof(JsonPropertyAttribute), true).Any())
                    {
                        property.Ignored = false;
                        return property;
                    }
                }
            }
        }

        return property;
    }
}

使用InterfaceContractResolver在我的JsonSerializerSettings中,任何接口中具有JsonIgnoreAttribute的属性也会被忽略,即使它们具有JsonPropertyAttribute(由于内部if块的顺序)。


5
在最近版本的Json.NET中,将[JsonIgnore]应用于接口属性现在可以正常工作,并成功防止它们被序列化为所有实现类型,只要该属性在声明接口的同一类中声明。不再需要自定义合同解析器。
例如,如果我们定义以下类型:
public interface IFoo 
{
    [JsonIgnore]
    string SecretProperty  { get; set; }

    string Include { get; set; }
}

public class Foo : IFoo 
{
    public string SecretProperty  { get; set; }
    public string Include { get; set; }
}

然后在Json.NET 11和12(以及可能更早的版本)中,以下测试通过:

var root = new Foo
{
    SecretProperty  = "Ignore Me",
    Include = "Include Me",
};

var json = JsonConvert.SerializeObject(root);

Assert.IsTrue(json == "{\"Include\":\"Include Me\"}");// Passes

演示代码可以在这里这里找到。

我相信这是在Json.NET 4.0.3中添加的,尽管在发布说明中没有明确提到JsonIgnore

新功能 - JsonObject和JsonProperty属性现在可以放置在接口上,并在序列化实现对象时使用。

(该实现可以在JsonTypeReflector.GetAttribute<T>(MemberInfo memberInfo)中找到。)

然而,正如notedVitaly所指出的那样,在接口声明的类的基类中继承属性时,这种方法是行不通的。演示fiddle在这里


感谢您的更新。我不查找的话,我不知道我们当时使用的是哪个版本(已经7年了?!),但是在我们涉及的项目中,我们当前正在使用11.0.2版本,所以我想这将列入下一个重构会议的待办事项清单。 - fero
3
如果属性是从基类继承而来,则它无法工作。已在版本10和12中进行测试。这里有演示:https://dotnetfiddle.net/eliVCu - Vitaly
1
@Vitaly - 看起来你是对的。回答已更新。 - dbc
是的,但当JsonIgnore在接口中时它不起作用。这就是问题所在。 - madd
@madd - 你能分享一个 [mcve] 吗?这里有两个演示 fiddles https://dotnetfiddle.net/DcupZb 和 https://dotnetfiddle.net/WlagpX,展示了它的工作原理,所以知道它不起作用的时候会很有帮助。 - dbc

2

我发现创建一个仅包含我想要的属性的DTO并将该对象序列化为JSON最简单。这样可以创建许多小的、上下文特定的对象,但管理代码库更容易,而且我不必考虑正在序列化的内容和忽略的内容。


我一直在寻找更巧妙的方法来做这个,但担心最终只能用这种方式来实现。:o) - Ben Power

-1

你应该在类名前面添加[DataContract]

这会将默认值从包括所有属性更改为仅包括显式标记的属性。之后,在要包含在JSON输出中的每个属性前面添加[DataMember]


这绝对不是我想要的。我想在接口中定义一个忽略属性,将其传递到类层次结构的顶部,以便序列化程序将忽略我的接口属性。我不希望该属性在接口的任何实现中可序列化。 - fero

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