Как наследовать атрибут от интерфейса к объекту при его сериализации с использованием JSON.NET

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using Newtonsoft.Json.Serialization;
using System.Linq;
using System.Reflection;
public interface IParent
{
    [JsonProperty]
    int Id {get;set;}
}

[JsonObject(MemberSerialization.OptIn)]
public class Parent : IParent
{
    public int Id { get;set; }  
    public string Name {get;set;}   
}

public class Serializer
{
    public static void Main()
    {

        var parent = new Parent() { Id = 1, Name ="Parent"};        
        var sb = new StringBuilder();
                var sw = new StringWriter(sb);

                var settings = new JsonSerializerSettings()
                       {
                           NullValueHandling = NullValueHandling.Ignore                            
                       };

            var output = JsonConvert.SerializeObject(parent, Formatting.None, settings);
                Console.WriteLine(output);
            Console.ReadKey();
    }
}

В приведенном выше коде вывод {}. Можно ли сериализовать и получить вывод как {"Id":1}?


person Gopal    schedule 10.05.2011    source источник
comment
Поддержка [JsonProperty] в объявлениях свойств интерфейса реализована с Json.NET 4.0.3, так что теперь этот код просто работает, см. dotnetfiddle.net/bEIWEF. Таким образом, теперь это дубликат Получение SerializeObject для использования «имени» JsonProperty, определенного внутри интерфейса.   -  person dbc    schedule 04.04.2019


Ответы (2)


Это плохая идея.

Сказав это, Newtonsoft предоставляет вам способ изменить то, что сериализовано: в этом случае вы должны создать подкласс DefaultContractResolver и переопределить CreateProperty.

Проблема в том, что нелегко решить, когда следует согласиться на сериализацию на основе атрибутов интерфейса. Во-первых, класс потенциально может реализовать несколько интерфейсов с конфликтующими инструкциями сериализации. Кроме того, десериализация объекта в переменную, объявленную как конфликтующий интерфейс (например), не будет работать. Он хрупок и небезопасен (он позволяет внешнему коду указывать, какие данные раскрывает экземпляр).

Если вам нужно это сделать, приведенный ниже код работает для вашего случая:

public class InterfaceContractResolver : DefaultContractResolver, IContractResolver
{
    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(JsonPropertyAttribute), true).Any())
                    {
                        property.Ignored = false;
                        return property;
                    }
                }
            }
        }
        return property;
    }

}

Затем, когда вы создаете свой сериализатор, передайте ему экземпляр вашего преобразователя в настройках:

var settings = new JsonSerializerSettings()
{
    NullValueHandling = NullValueHandling.Ignore,
    ContractResolver = new InterfaceContractResolver(true)
};
person Jeff Sternal    schedule 11.05.2011
comment
Спасибо, Джефф, я искал что-то подобное. - person Gopal; 11.05.2011
comment
Меня всегда поражала гибкость Json.NET. Приятно осознавать, что если мне когда-нибудь понадобится поджемовать, чтобы заставить это работать, я смогу. - person Frank; 11.05.2011
comment
Одним из способов сделать тысячу менее изменчивой было бы создание атрибута SerializeAs, который заставляет вас указывать только один интерфейс. Это или предварительное сканирование на наличие конфликтов. - person George R; 26.11.2012

См. здесь... Я не верю, что это работает. Атрибуты JsonProperty в интерфейсе игнорируются для объекта, реализующего интерфейс.

person Frank    schedule 10.05.2011
comment
Спасибо за ссылку, откровенно говоря, я понимаю, что атрибуты не наследуются от интерфейсов к реализации, так как они страдают проблемой множественного наследования. - person Gopal; 11.05.2011
comment
Этот ответ устарел, на связанный вопрос теперь есть ответ, в котором говорится, что это было реализовано в Json.NET 4.0.3. - person dbc; 04.04.2019