Подтвердить что ты не робот

Сериализовать только свойства интерфейса для JSON с помощью Json.net

С простым классом/интерфейсом, подобным этому

public interface IThing
{
    string Name { get; set; }
}

public class Thing : IThing
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Как я могу получить строку JSON только с свойством "Name" (только свойства базового интерфейса)?

Собственно, когда я делаю это:

var serialized = JsonConvert.SerializeObject((IThing)theObjToSerialize, Formatting.Indented);
Console.WriteLine(serialized);

Я получаю полный объект как JSON (Id + Name);

Спасибо заранее!

4b9b3361

Ответ 1

Вы можете использовать условную сериализацию. Посмотрите на ссылку . В основном вам нужно реализовать интерфейс IContractResolver, перегрузить метод ShouldSerialize и передать свой резольвер в конструктор Json Serializer.

Ответ 2

Метод, который я использую,

public class InterfaceContractResolver : DefaultContractResolver
{
    private readonly Type _InterfaceType;
    public InterfaceContractResolver (Type InterfaceType)
    {
        _InterfaceType = InterfaceType;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        //IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
        IList<JsonProperty> properties = base.CreateProperties(_InterfaceType, memberSerialization);
        return properties;
    }
}

// To serialize do this:
var settings = new JsonSerializerSettings() {
     ContractResolver = new InterfaceContractResolver (typeof(IThing))
});
string json = JsonConvert.SerializeObject(theObjToSerialize, settings);

Ответ 3

Вдохновленный @user3161686, здесь небольшая модификация InterfaceContractResolver:

public class InterfaceContractResolver<TInterface> : DefaultContractResolver where TInterface : class
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> properties = base.CreateProperties(typeof(TInterface), memberSerialization);
        return properties;
    }
}

Ответ 4

Вы можете добавить аннотацию [JsonIgnore], чтобы игнорировать атрибут.

Ответ 5

Альтернативой [JsonIgnore] являются [DataContract] и [DataMember]. Если ваш класс помечен тегом [DataContract], сериализатор будет обрабатывать только теги с атрибутом [DataMember] (JsonIgnore - это модель отказа, а DataContract - "op-in" ).

[DataContract]
public class Thing : IThing
{
    [DataMember]
    public int Id { get; set; }

    public string Name { get; set; }
}

Ограничение обоих подходов состоит в том, что они должны быть реализованы в классе, вы не можете добавить их в определение интерфейса.

Ответ 6

Улучшенная версия с вложенными интерфейсами + поддержка объектов xsd.exe

Еще одна вариация здесь. Код пришел из http://www.tomdupont.net/2015/09/how-to-only-serialize-interface.html со следующими улучшениями по сравнению с другими ответами здесь

  • Обрабатывает иерархию, поэтому, если у вас есть Interface2[] внутри Interface1, тогда она будет сериализована.
  • Я пытался сериализовать прокси-объект WCF, и полученный JSON появился как {}. Оказалось, что все свойства были установлены на Ignore=true, поэтому мне пришлось добавить цикл, чтобы все они не игнорировались.

    public class InterfaceContractResolver : DefaultContractResolver
    {
        private readonly Type[] _interfaceTypes;
    
        private readonly ConcurrentDictionary<Type, Type> _typeToSerializeMap;
    
        public InterfaceContractResolver(params Type[] interfaceTypes)
        {
            _interfaceTypes = interfaceTypes;
    
            _typeToSerializeMap = new ConcurrentDictionary<Type, Type>();
        }
    
        protected override IList<JsonProperty> CreateProperties(
            Type type,
            MemberSerialization memberSerialization)
        {
            var typeToSerialize = _typeToSerializeMap.GetOrAdd(
                type,
                t => _interfaceTypes.FirstOrDefault(
                    it => it.IsAssignableFrom(t)) ?? t);
    
            var props = base.CreateProperties(typeToSerialize, memberSerialization);
    
            // mark all props as not ignored
            foreach (var prop in props)
            {
                prop.Ignored = false;
            }
    
            return props;
        }
    }
    

Ответ 8

Наконец-то я получил, когда это не сработает... Если вы хотите иметь внутри другого сложного объекта, он не будет правильно сериализован.

Итак, я сделал версию, которая будет извлекать только данные, хранящиеся в конкретной сборке, и для типов, которые имеют один и тот же базовый интерфейс.

Итак, он сделан как .Net Core JsonContractResolver.

В дополнение к извлечению данных он решает:
a) преобразование camelCase перед отправкой данных клиенту
б) использует самый верхний интерфейс из разрешенной области (путем сборки) c) исправляет порядок полей: поле из большинства базовых классов будет указано первым, а вложенный объект также будет соответствовать этому правилу.

public class OutputJsonResolver : DefaultContractResolver
{
    #region Static Members
    private static readonly object syncTargets = new object();
    private static readonly Dictionary<Type, IList<JsonProperty>> Targets = new Dictionary<Type, IList<JsonProperty>>();

    private static readonly Assembly CommonAssembly = typeof(ICommon).Assembly;
    #endregion

    #region Override Members
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        if (type.Assembly != OutputJsonResolver.CommonAssembly)
            return base.CreateProperties(type, memberSerialization);

        IList<JsonProperty> properties;
        if (OutputJsonResolver.Targets.TryGetValue(type, out properties) == false)
        {
            lock (OutputJsonResolver.syncTargets)
            {
                if (OutputJsonResolver.Targets.ContainsKey(type) == false)
                {
                    properties = this.CreateCustomProperties(type, memberSerialization);

                    OutputJsonResolver.Targets[type] = properties;
                }
            }
        }

        return properties;
    }
    protected override string ResolvePropertyName(string propertyName)
    {
        return propertyName.ToCase(Casing.Camel);
    }
    #endregion

    #region Assistants
    private IList<JsonProperty> CreateCustomProperties(Type type, MemberSerialization memberSerialization)
    {
        // Hierarchy
        IReadOnlyList<Type> types = this.GetTypes(type);

        // Head
        Type head = types.OrderByDescending(item => item.GetInterfaces().Length).FirstOrDefault();

        // Sources
        IList<JsonProperty> sources = base.CreateProperties(head, memberSerialization);

        // Targets
        IList<JsonProperty> targets = new List<JsonProperty>(sources.Count);

        // Repository
        IReadOnlyDistribution<Type, JsonProperty> repository = sources.ToDistribution(item => item.DeclaringType);

        foreach (Type current in types.Reverse())
        {
            IReadOnlyPage<JsonProperty> page;
            if (repository.TryGetValue(current, out page) == true)
                targets.AddRange(page);
        }

        return targets;
    }
    private IReadOnlyList<Type> GetTypes(Type type)
    {
        List<Type> types = new List<Type>();

        if (type.IsInterface == true)
            types.Add(type);

        types.AddRange(type.GetInterfaces());

        return types;
    }
    #endregion
}