Désérialiser le dictionnaire avec les valeurs enum en C #

J’essaie de sérialiser / désérialiser un Dictionary en C #. L’object peut être tout ce qui est sérialisable.

Json.NET fonctionne presque, mais si une valeur dans le dictionnaire est une enum , la désérialisation n’est pas correcte car elle est désérialisée trop long . TypeNameHandling.All ne fait aucune différence.

Existe-t-il une autre solution rapide à la bibliothèque de sérialisation? Le résultat ne doit pas obligatoirement être JSON, mais doit être du texte.

Je n’ai également aucune influence sur les données transmises au dictionnaire. Je dois juste sérialiser et désérialiser tout ce qui me gêne.

EDIT: SsortingngEnumConverter ne aide pas. Les données sont reconverties en Dictionary , de sorte que le désérialiseur ne sait pas que la valeur sérialisée est un enum . Il le traite comme un object. Avec SsortingngEnumConverter il rest une ssortingng lorsqu’il est désérialisé. il se désérialise aussi long sans le convertisseur. JSON.NET ne conserve pas l’énumération.

La solution que je souhaite fournir est la mise en œuvre d’une interface existante qui est injectée dans une solution existante et que je ne peux pas modifier.

EDIT2: Voici un exemple de ce que je suis en train de faire

 public enum Foo { A, B, C } public enum Bar { A, B, C } public class Misc { public Foo Foo { get; set; } } var dict = new Dictionary(); dict.Add("a", Foo.A); dict.Add("b", Bar.B); dict.Add("c", new Misc()); // serialize dict to a ssortingng s // deserialize s to a Dictionary dict2 Assert.AreEqual(Foo.A, dict2["a"]); Assert.AreEqual(Bar.B, dict2["b"]); 

Important: je ne peux pas contrôler le dict ; il s’agit en fait d’un type personnalisé dérivé de Dictionary : je dois simplement m’assurer que toutes les clés et valeurs désérialisées proviennent du même type lorsqu’elles sont désérialisées, de sorte qu’aucun transtypage n’est nécessaire. Et encore une fois, je n’ai pas besoin d’utiliser JSON; peut-être y at-il un autre sérialiseur capable de gérer le travail!?

Vous êtes probablement en train de sérialiser votre dictionnaire avec TypeNameHandling.All , ce qui devrait sérialiser et désérialiser correctement la new Misc() valeur new Misc() en émettant une propriété d’object "$type" avec l’object lui-même. Malheureusement, pour les types tels que enums (et d’autres tels que int et long ), cela ne fonctionne pas car ils sont sérialisés en tant que primitives JSON, sans possibilité d’inclure une propriété "$type" .

La solution consiste, lors de la sérialisation d’un dictionnaire avec des valeurs d’ object , à sérialiser les wrappers d’object pour les valeurs primitives pouvant encapsuler les informations de type, le long des lignes de cette réponse . Étant donné que vous ne pouvez modifier aucun de vos objects entrants et que vous devez “injecter” les wrappers appropriés, vous pouvez le faire avec un résolveur de contrat personnalisé qui applique un convertisseur d’élément approprié aux valeurs du dictionnaire:

 public class UntypedToTypedValueContractResolver : DefaultContractResolver { // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons. // http://www.newtonsoft.com/json/help/html/ContractResolver.htm // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance." // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information static UntypedToTypedValueContractResolver instance; // Explicit static constructor to tell C# comstackr not to mark type as beforefieldinit static UntypedToTypedValueContractResolver() { instance = new UntypedToTypedValueContractResolver(); } public static UntypedToTypedValueContractResolver Instance { get { return instance; } } protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) { var contract = base.CreateDictionaryContract(objectType); if (contract.DictionaryValueType == typeof(object) && contract.ItemConverter == null) { contract.ItemConverter = new UntypedToTypedValueConverter(); } return contract; } } class UntypedToTypedValueConverter : JsonConverter { public override bool CanConvert(Type objectType) { throw new NotImplementedException("This converter should only be applied directly via ItemConverterType, not added to JsonSerializer.Converters"); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var value = serializer.Deserialize(reader, objectType); if (value is TypeWrapper) { return ((TypeWrapper)value).ObjectValue; } return value; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (serializer.TypeNameHandling == TypeNameHandling.None) { Debug.WriteLine("ObjectItemConverter used when serializer.TypeNameHandling == TypeNameHandling.None"); serializer.Serialize(writer, value); } // Handle a couple of simple primitive cases where a type wrapper is not needed else if (value is ssortingng) { writer.WriteValue((ssortingng)value); } else if (value is bool) { writer.WriteValue((bool)value); } else { var contract = serializer.ContractResolver.ResolveContract(value.GetType()); if (contract is JsonPrimitiveContract) { var wrapper = TypeWrapper.CreateWrapper(value); serializer.Serialize(writer, wrapper, typeof(object)); } else { serializer.Serialize(writer, value); } } } } public abstract class TypeWrapper { protected TypeWrapper() { } [JsonIgnore] public abstract object ObjectValue { get; } public static TypeWrapper CreateWrapper(T value) { if (value == null) return new TypeWrapper(); var type = value.GetType(); if (type == typeof(T)) return new TypeWrapper(value); // Return actual type of subclass return (TypeWrapper)Activator.CreateInstance(typeof(TypeWrapper<>).MakeGenericType(type), value); } } public sealed class TypeWrapper : TypeWrapper { public TypeWrapper() : base() { } public TypeWrapper(T value) : base() { this.Value = value; } public override object ObjectValue { get { return Value; } } public T Value { get; set; } } 

Alors utilisez-le comme:

 var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, ContractResolver = UntypedToTypedValueContractResolver.Instance, Converters = new [] { new SsortingngEnumConverter() }, // If you prefer }; var json = JsonConvert.SerializeObject(dict, Formatting.Indented, settings); var dict2 = JsonConvert.DeserializeObject>(json, settings); 

Échantillon de violon .

Enfin, lorsque vous utilisez TypeNameHandling , tenez compte de cet avertissement de la documentation Newtonsoft :

TypeNameHandling doit être utilisé avec prudence lorsque votre application désérialise JSON à partir d’une source externe. Les types entrants doivent être validés avec un SerializationBinder personnalisé lors de la désérialisation avec une valeur autre que Aucun.

Pour une discussion sur les raisons pour lesquelles cela peut être nécessaire, reportez-vous à la rubrique Mise en garde sur TypeNameHandling dans Newtonsoft Json .

En supposant que vous ayez access à la modification de la classe d’ object , vous pouvez append l’atsortingbut JsonCoverter au membre enum de la classe.

 [JsonConverter(typeof(SsortingngEnumConverter))]