web-dev-qa-db-fra.com

Configurer JSON.NET pour ignorer les attributs DataContract / DataMember

Nous rencontrons une situation sur un projet MVC3 avec les sérialiseurs Microsoft JSON et JSON.NET.

Tout le monde sait que DateTime est fondamentalement cassé dans les sérialiseurs de Microsoft, nous avons donc opté pour JSON.NET pour éviter ce problème. Cela fonctionne très bien, sauf que certaines des classes que nous essayons de sérialiser sont des POCO avec des attributs DataContract/DataMember. Ils sont définis dans un assembly référencé à plusieurs endroits. En outre, ils ont d'autres propriétés d'affichage qui ne sont pas marquées comme DataMembers pour plus d'efficacité. Par exemple, un client

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

}

Lorsque ce client est passé sur WCF, le côté client peut référencer cet assembly et utiliser le nom complet très bien, mais lorsqu'il est sérialisé avec JSON.NET, il voit que le nom complet n'est pas un [DataMember] et ne le sérialise pas. Existe-t-il une option à passer à JSON.NET pour lui dire d'ignorer le fait qu'une classe a [DataContract] attribut appliqué?

Remarque: L'utilisation de JavaScriptSerializer dans .NET fonctionne correctement pour la propriété FullName, mais DateTimes sont rompus. J'ai besoin de JSON.NET pour ignorer le fait que cette classe a des attributs DataContract/DataMember et juste faire une sérialisation de champ public standard comme si ce n'était pas le cas.

35
Nick

Utilisez simplement l'attribut OptOut de Json.Net. Il prévaudra sur DataContract.

[DataContract]
[JsonObject(MemberSerialization.OptOut)]
31
seldary

Comme l'a dit Amry, vous pouvez utiliser votre propre IContractResolver.

Malheureusement, la solution fournie par Amry n'a pas fonctionné pour moi, voici la solution que j'ai réussi à faire fonctionner:

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

Il y a quelques lignes commentées, celles-ci ne sont pas nécessaires pour faire fonctionner ma solution, mais on ne sait jamais!

Cela a le même usage que la solution d'Amry:

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

J'espère que cela t'aides!

18
Doolali

J'avais un problème presque lié à ce que vous rencontrez et j'ai réussi à trouver une solution en parcourant les codes de Json.NET. Ce n'est peut-être pas la meilleure solution, mais cela fonctionne pour moi.

Pour ce faire, vous devez implémenter votre propre IContractResolver. Une implémentation trop simplifiée de cela pour inclure tous les paramètres et ignorer tous les attributs (pas seulement DataContract mais aussi les autres règles de Json.NET intégrées, donc toutes les options que vous définissez qui devraient affecter à l'origine la sélection des membres sont maintenant étant remplacé par ce code):

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

Et voici l'exemple d'utilisation du code:

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

Conformément à la documentation Json.NET [DataMember] les attributs sont ignorés si les propriétés sont également annotées avec des attributs spécifiques à Json.NET (tels que [JsonProperty]). Voir la documentation Serialization Attributes pour plus de détails:

Les attributs Json.NET prennent le pas sur les attributs de sérialisation .NET standard, par ex. si JsonPropertyAttribute et DataMemberAttribute sont présents sur une propriété et personnalisent le nom, le nom de JsonPropertyAttribute sera utilisé.

La documentation ne couvre que la propriété name, mais d'après mon expérience, le [JsonProperty] l'attribut ombres également entièrement les paramètres effectués par le [DataMember] attribut. Donc, si cela est possible pour votre cas, ajoutez également des attributs Json.NET aux propriétés pour lesquelles l'annotation [DataMember] doit être ignorée.

8
rene

Si vous voulez ignorer la présence de DataContractAttribute pour tous les types sans avoir à ajouter d'attributs supplémentaires, alors un résolveur de contrat personnalisé est la bonne solution. Cependant, depuis Json.NET 9.0.1 le résolveur d'Amry ne fonctionne plus. le résolveur de Doolali fonctionne mais il a pour effet secondaire supplémentaire de sérialiser toutes les propriétés publiques , y compris celles marquées [JsonIgnore] . Si vous avez besoin d'un résolveur de contrat qui uniquement ignore la présence de DataContractAttribute mais se comporte autrement comme le résolveur de contrat par défaut, ce qui suit peut être utilisé :

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

Vous voudrez peut-être mettre en cache le résolveur de contrat pour de meilleures performances .

8
dbc