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

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

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

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

[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 и просто выполняет стандартную сериализацию в публичном поле, как если бы они не были там.

4b9b3361

Ответ 1

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

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

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;
    }
}

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

Это то же самое, что и решение Amry:

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

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

Ответ 2

У меня была проблема, почти связанная с тем, что у вас есть, и удалось найти решение, пройдя коды 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()
});

Ответ 3

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

[DataContract]
[JsonObject(MemberSerialization.OptOut)]

Ответ 4

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

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

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

Ответ 5

Если вы хотите игнорировать присутствие DataContractAttribute для всех типов без добавления дополнительных атрибутов, то пользовательский разрешитель контрактов является правильное решение. Однако, как Json.NET 9.0.1 Amry resolver больше не работает. Doolali resolver работает, но имеет дополнительный побочный эффект сериализации всех публичных свойств, в том числе отмеченных [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;
        }
    }
}

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