Игнорировать свойство, определенное в интерфейсе при сериализации с использованием JSON.net

У меня есть интерфейс с таким свойством:

public interface IFoo {
    // ...

    [JsonIgnore]
    string SecretProperty { get; }

    // ...
}

Я хочу, чтобы SecretProperty игнорировался при сериализации всех реализующих классов. Но мне кажется, что я должен определить атрибут JsonIgnore для каждой реализации свойства. Есть ли способ достичь этого, не JsonIgnore атрибут JsonIgnore к каждой реализации? Я не нашел настройки сериализатора, которые мне помогли.

Ответ 1

После небольшого поиска я нашел этот вопрос:

Как наследовать атрибут от интерфейса к объекту при его сериализации с помощью 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).

Ответ 2

Мне было проще прописать DTO только те свойства, которые я хочу, и сериализовать этот объект в JSON. он создает множество небольших объектов, специфичных для контекста, но управление базой кода намного проще, и мне не нужно думать о том, что я сериализую против того, что я игнорирую.

Ответ 3

В более поздних версиях 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).)

Однако, как отметил Виталием , это не работает, когда свойство наследуется от базового класса класса, в котором объявлен интерфейс. Демонстрационная скрипка здесь.

Ответ 4

Вы должны добавить [DataContract] перед именем класса.

Он изменяет значение по умолчанию из всех свойств, включая только явно помеченные свойства. После этого добавьте "[DataMember]" перед каждым свойством, которое вы хотите включить в выход JSON.