Настройте JSON.NET на игнорирование атрибутов DataContract / DataMember

Мы сталкиваемся с ситуацией в проекте MVC3 с сериализаторами Microsoft JSON и JSON.NET.

Всем известно, что DateTime в сериализаторах Microsoft в основном не работает, поэтому мы перешли на JSON.NET, чтобы избежать этой проблемы. Это отлично работает, за исключением того, что некоторые из классов, которые мы пытаемся сериализовать, являются POCO с атрибутами DataContract / DataMember. Они определены в сборке, на которую есть ссылки в нескольких местах. Кроме того, у них есть некоторые другие свойства отображения, которые для эффективности не помечены как DataMembers. Например, Заказчик

[DataContract]
public class Customer
{
   [DataMember]
   public string FirstName { get; set;}
   [DataMember]
   public string LastName { get; set;}
   public string FullName 
   {
       get
       {  return FirstName + " " + LastName; }
   }

}

Когда этот клиент передается через WCF, клиентская сторона может ссылаться на эту сборку и использовать FullName нормально, но при сериализации с помощью JSON.NET он видит, что FullName не является [DataMember], и не сериализует его. Есть ли возможность передать JSON.NET, чтобы он игнорировал тот факт, что к классу применен атрибут [DataContract]?

Примечание. Использование JavaScriptSerializer в .NET отлично работает для свойства FullName, но DateTimes не работает. Мне нужно, чтобы JSON.NET игнорировал тот факт, что этот класс имеет атрибуты DataContract / DataMember, и просто выполнял стандартную сериализацию общедоступных полей, как если бы их не было.


person Nick    schedule 15.06.2012    source источник
comment
Вы разрешили это? У меня такая же проблема, и мне нужно найти решение   -  person Kendall Bennett    schedule 10.07.2012
comment
В итоге я добавил атрибут JsonProperty для Json.Net   -  person Nick    schedule 09.08.2012


Ответы (6)


Просто используйте атрибут OptOut Json.Net. Он будет иметь приоритет над DataContract.

[DataContract]
[JsonObject(MemberSerialization.OptOut)]
person seldary    schedule 11.05.2014
comment
Огромное спасибо. Это должно быть отмечено как правильный ответ. Я работал над очень старым приложением (WCF с RESTFul api) и хотел сериализовать некоторые данные, чтобы передать их новой веб-службе API. это решение работало как шарм. Голосование вверх. - person DragoRaptor; 08.06.2018
comment
Спасибо. Это очень хорошо решило мою проблему в некотором роде. Я вижу использование атрибута [JsonProperty] в следующем ответе для большей детализации. - person Wade Hatler; 20.05.2019
comment
К сожалению, если вы также хотите использовать сериализацию для JSON, атрибут [DataMember] приведет к сериализации свойства в JSON. Если это нежелательно, вы должны добавить [JsonIgnore] к каждому свойству с атрибутом [DataMember] или использовать одно из решений, включающих настраиваемый преобразователь контрактов. - person Florian Winter; 12.02.2021

Как сказала Амри, вы можете использовать свой собственный IContractResolver.

К сожалению, решение, предоставленное Амри, не сработало для меня, ниже представлено решение, которое мне удалось заставить работать:

public class AllPropertiesResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        //property.HasMemberAttribute = true;
        property.Ignored = false;

        //property.ShouldSerialize = instance =>
        //{
        //    return true;
        //};

        return property;
    }
}

Есть несколько прокомментированных строк, они не нужны, чтобы мое решение работало, но вы никогда не узнаете!

Он используется так же, как и решение Амри:

var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings {
    ContractResolver = new AllPropertiesResolver()
});

Надеюсь это поможет!

person Doolali    schedule 17.02.2014

У меня была проблема, почти связанная с тем, что у вас есть, и мне удалось найти решение, просмотрев коды Json.NET. Возможно, это не лучшее решение, но оно работает для меня.

Для этого нужно реализовать свой IContractResolver. Чрезмерно упрощенная реализация этого, включающая все параметры и игнорирующая все атрибуты (не только DataContract, но и другие встроенные правила Json.NET, так что любые установленные вами параметры, которые изначально должны влиять на выбор членов, теперь перекрываются этим кодом. ):

class AllPropertiesResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        return objectType.GetProperties()
            .Where(p => p.GetIndexParameters().Length == 0)
            .Cast<MemberInfo>()
            .ToList();
    }
}

А вот пример использования кода:

var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings {
    ContractResolver = new AllPropertiesResolver()
});
person Amry    schedule 28.07.2012
comment
Здорово. Почему нет? Что не работает? Помогите нам помочь вам. - person Cornelius; 09.06.2017
comment
Этот подход не сработал и для меня, Амри, тебе нужно было что-нибудь еще добавить, чтобы это заработало? - person Scott; 12.10.2017
comment
Этот ответ работал у меня для версии Json.NET еще в 2012 году. Я не знаю, будет ли он работать сейчас в 2017 году (похоже, что он больше не работает), и, к сожалению, у меня нет личной необходимости использовать его сейчас в 2017 году, поэтому я не буду предлагать работоспособное решение. - person Amry; 21.10.2017

В соответствии с документацией Json.NET атрибуты [DataMember] игнорируются, если свойства также аннотированы специфическими атрибутами Json.NET (такими как _2 _). См. в документации по атрибутам сериализации:

Атрибуты Json.NET имеют приоритет над стандартными атрибутами сериализации .NET, например если в свойстве присутствуют и JsonPropertyAttribute, и DataMemberAttribute, и оба настраивают имя, будет использоваться имя из JsonPropertyAttribute.

В документации рассматривается только свойство name, но, по моему опыту, атрибут [JsonProperty] также полностью затеняет настройки, выполняемые атрибутом [DataMember]. Итак, если это возможно для вашего случая, также добавьте атрибуты Json.NET к свойствам, для которых аннотация [DataMember] должна игнорироваться.

person rene    schedule 08.04.2014
comment
Из вашего ответа я узнал, что если класс имеет атрибут [DataContract], тогда он устанавливает для сериализации членов значение opt-in, что означает, что любой член (field или свойство) ДОЛЖЕН иметь атрибут JsonProperty или [DataMember] для сериализации (в противном случае он будет проигнорирован). Я потратил часы, чтобы найти эту информацию. - person stomy; 19.09.2019

Если вы хотите игнорировать наличие DataContractAttribute для всех типов без добавления дополнительных атрибутов, тогда настраиваемый преобразователь контрактов - правильное решение. Однако с Json.NET 9.0.1 преобразователь Амри больше не работает. преобразователь Doolali работает, но имеет дополнительный побочный эффект сериализации всех общедоступных свойств, включая те, которые отмечены [JsonIgnore]. Если вам требуется преобразователь контрактов, который только игнорирует наличие DataContractAttribute, но в остальном ведет себя как преобразователь контрактов по умолчанию, можно использовать следующее:

public class IgnoreDataContractContractResolver : DefaultContractResolver
{
    static MemberSerialization RemoveDataContractAttributeMemberSerialization(Type type, MemberSerialization memberSerialization)
    {
        if (memberSerialization == MemberSerialization.OptIn)
        {
            type = Nullable.GetUnderlyingType(type) ?? type;

            // Json.NET interprets DataContractAttribute as inherited despite the fact it is marked with Inherited = false
            // https://json.codeplex.com/discussions/357850
            // https://stackoverflow.com/questions/8555089/datacontract-and-inheritance
            // https://github.com/JamesNK/Newtonsoft.Json/issues/603
            // Thus we need to manually climb the type hierarchy to see if one is present.

            var dataContractAttribute = type.BaseTypesAndSelf().Select(t => t.GetCustomAttribute<DataContractAttribute>()).FirstOrDefault(a => a != null);
            var jsonObjectAttribute = type.GetCustomAttribute<JsonObjectAttribute>();

            if (dataContractAttribute != null && jsonObjectAttribute == null)
                memberSerialization = MemberSerialization.OptOut;
        }
        return memberSerialization;
    }

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

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var contract = base.CreateObjectContract(objectType);
        contract.MemberSerialization = RemoveDataContractAttributeMemberSerialization(objectType, contract.MemberSerialization);
        return contract;
    }
}

public static class TypeExtensions
{
    public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
    {
        while (type != null)
        {
            yield return type;
            type = type.BaseType;
        }
    }
}

Вы можете захотеть кэшировать распознаватель контрактов для лучшей производительности.

person dbc    schedule 02.09.2016
comment
@HappyNomad - DefaultContractResolver не предоставляет простой способ вставки атрибутов. Я отменяю CreateProperties(), чтобы передать OptOut, но значение contract.MemberSerialization все равно возвращалось как OptIn. Поскольку это казалось непоследовательным, я также решил переопределить CreateObjectContract(). - person dbc; 17.09.2016
comment
Хорошее исправление, но я заметил, что RemoveDataContractAttributeMemberSerialization бесполезно вызывается дважды для каждого типа. Это потому, что base.CreateObjectContract вызывает CreateProperties. Я отправил проблему в JSON.NET по этому поводу. - person HappyNomad; 17.09.2016
comment
Да, я заметил это сразу после публикации своего комментария и поэтому переписал свой комментарий. - person HappyNomad; 17.09.2016

Вы пробовали это?

IgnoreDataMemberAttribute

person duckus    schedule 15.06.2012
comment
Я хочу сделать противоположное игнорированию поля, я хочу, чтобы поле было включено при сериализации с использованием JSON.NET - person Nick; 15.06.2012