Désérialiser json avec des champs connus et inconnus

Résultat json suivant: Le résultat par défaut de json a un ensemble de champs connu:

{ "id": "7908", "name": "product name" } 

Mais vous pouvez étendre avec des champs supplémentaires (dans cet exemple _unknown_field_name_1 et _unknown_field_name_2 ) dont les noms ne sont pas connus lors de la demande du résultat.

 { "id": "7908", "name": "product name", "_unknown_field_name_1": "some value", "_unknown_field_name_2": "some value" } 

Je voudrais que le résultat de json soit sérialisé et désérialisé vers et depuis une classe avec des propriétés pour les champs connus et mapper les champs inconnus (pour lesquels il n’y a pas de propriétés) à une propriété (ou plusieurs propriétés) comme un dictionnaire afin qu’ils puissent être accédé et modifié.

 public class Product { public ssortingng id { get; set; } public ssortingng name { get; set; } public Dictionary fields { get; set; } } 

Je pense que j’ai besoin d’un moyen de twigr un sérialiseur json et de faire le mappage des membres manquants (à la fois pour la sérialisation et la désérialisation). J’ai examiné différentes possibilités:

  • json.net et résolveurs de contrats personnalisés (ne peut pas comprendre comment faire)
  • sérialiseur de contrat de données (ne peut remplacer que la numérisation en série, onserializing)
  • sérialiser à dynamic et faire un mappage personnalisé (cela pourrait fonctionner, mais semble beaucoup de travail)
  • laisser le produit hériter de DynamicObject (les sérialiseurs fonctionnent avec la reflection et n’appellent pas les méthodes trygetmember et trysetmember)

J’utilise restsharp, mais tout sérialiseur peut être branché.

Oh, et je ne peux pas changer le résultat json, et ceci ou cela ne m’a pas aidé non plus.

Mise à jour: Cela ressemble plus à cela: http://geekswithblogs.net/DavidHoerster/archive/2011/07/26/json.net-custom-convertersndasha-quick-tour.aspx

Une option encore plus simple pour résoudre ce problème consiste à utiliser JsonExtensionDataAtsortingbute de JSON .NET.

 public class MyClass { // known field public decimal TaxRate { get; set; } // extra fields [JsonExtensionData] private IDictionary _extraStuff; } 

Il y en a un exemple sur le blog du projet ici

MISE À JOUR Veuillez noter que cela nécessite JSON .NET v5 version 5 et supérieure

Voir https://gist.github.com/LodewijkSioen/5101814

Ce que vous cherchiez était un JsonConverter personnalisé

C’est une façon de le résoudre, même si cela ne me plaît pas beaucoup. Je l’ai résolu en utilisant Newton / JSON.Net. Je suppose que vous pouvez également utiliser le JsonConverter pour la désérialisation.

 private const ssortingng Json = "{\"id\":7908,\"name\":\"product name\",\"_unknown_field_name_1\":\"some value\",\"_unknown_field_name_2\":\"some value\"}"; [TestMethod] public void TestDeserializeUnknownMembers() { var @object = JObject.Parse(Json); var serializer = new Newtonsoft.Json.JsonSerializer(); serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Error; serializer.Error += (sender, eventArgs) => { var contract = eventArgs.CurrentObject as Contract ?? new Contract(); contract.UnknownValues.Add(eventArgs.ErrorContext.Member.ToSsortingng(), @object[eventArgs.ErrorContext.Member.ToSsortingng()].Value()); eventArgs.ErrorContext.Handled = true; }; using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(Json))) using (StreamReader streamReader = new StreamReader(memoryStream)) using (JsonReader jsonReader = new JsonTextReader(streamReader)) { var result = serializer.Deserialize(jsonReader); Assert.IsTrue(result.UnknownValues.ContainsKey("_unknown_field_name_1")); Assert.IsTrue(result.UnknownValues.ContainsKey("_unknown_field_name_2")); } } [TestMethod] public void TestSerializeUnknownMembers() { var deserializedObject = new Contract { id = 7908, name = "product name", UnknownValues = new Dictionary { {"_unknown_field_name_1", "some value"}, {"_unknown_field_name_2", "some value"} } }; var json = JsonConvert.SerializeObject(deserializedObject, new DictionaryConverter()); Console.WriteLine(Json); Console.WriteLine(json); Assert.AreEqual(Json, json); } } class DictionaryConverter : JsonConverter { public DictionaryConverter() { } public override bool CanConvert(Type objectType) { return objectType == typeof(Contract); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var contract = value as Contract; var json = JsonConvert.SerializeObject(value); var dictArray = Ssortingng.Join(",", contract.UnknownValues.Select(pair => "\"" + pair.Key + "\":\"" + pair.Value + "\"")); json = json.Subssortingng(0, json.Length - 1) + "," + dictArray + "}"; writer.WriteRaw(json); } } class Contract { public Contract() { UnknownValues = new Dictionary(); } public int id { get; set; } public ssortingng name { get; set; } [JsonIgnore] public Dictionary UnknownValues { get; set; } } 

}

Je pensais jeter mon chapeau sur le ring depuis un problème similaire récemment. Voici un exemple du JSON que je voulais désérialiser:

 { "agencyId": "agency1", "overrides": { "assumption.discount.rates": "value: 0.07", ".plan": { "plan1": { "assumption.payroll.growth": "value: 0.03", "provision.eeConsortingb.rate": "value: 0.35" }, "plan2": { ".classAndTier": { "misc:tier1": { "provision.eeConsortingb.rate": "value: 0.4" }, "misc:tier2": { "provision.eeConsortingb.rate": "value: 0.375" } } } } } } 

C’est pour un système où les remplacements s’appliquent à différents niveaux et sont hérités dans l’arborescence. En tout état de cause, le modèle de données que je voulais me permettait d’avoir un sac de propriété avec ces règles d’inheritance spéciales.

Ce que j’ai fini avec était le suivant:

 public class TestDataModel { public ssortingng AgencyId; public int Years; public PropertyBagModel Overrides; } public class ParticipantFilterModel { public ssortingng[] ClassAndTier; public ssortingng[] BargainingUnit; public ssortingng[] Department; } public class PropertyBagModel { [JsonExtensionData] private readonly Dictionary _extensionData = new Dictionary(); [JsonIgnore] public readonly Dictionary Values = new Dictionary(); [JsonProperty(".plan", NullValueHandling = NullValueHandling.Ignore)] public Dictionary ByPlan; [JsonProperty(".classAndTier", NullValueHandling = NullValueHandling.Ignore)] public Dictionary ByClassAndTier; [JsonProperty(".bargainingUnit", NullValueHandling = NullValueHandling.Ignore)] public Dictionary ByBarginingUnit; [OnSerializing] private void OnSerializing(StreamingContext context) { foreach (var kvp in Values) _extensionData.Add(kvp.Key, kvp.Value); } [OnSerialized] private void OnSerialized(StreamingContext context) { _extensionData.Clear(); } [OnDeserialized] private void OnDeserialized(StreamingContext context) { Values.Clear(); foreach (var kvp in _extensionData.Where(x => x.Value.Type == JTokenType.Ssortingng)) Values.Add(kvp.Key, kvp.Value.Value()); _extensionData.Clear(); } } 

L’idée de base est la suivante:

  1. Le PropertyBagModel lors de la désérialisation par JSON.NET contient les champs ByPlan, ByClassAndTier, etc., ainsi que le champ privé _extensionData.
  2. Ensuite, JSON.NET appelle la méthode OnDeserialized () privée et déplacera les données de _extensionData vers Values ​​selon les besoins (ou les déposera sur le sol sinon – vous pourriez probablement les enregistrer si cela était nécessaire). Nous retirons ensuite le gunk supplémentaire de _extensionData pour qu’il ne consum pas de mémoire.
  3. Lors de la sérialisation, la méthode OnSerializing obtient des appels où nous déplaçons des éléments dans _extensionData pour les enregistrer.
  4. Lorsque la sérialisation est terminée, OnSerialized est appelé et nous supprimons les éléments supplémentaires de _extensionData.

Nous pourrions encore supprimer et recréer le dictionnaire _extensionData en cas de besoin, mais je n’ai pas vu de réelle valeur car je n’utilise pas des tonnes de ces objects. Pour ce faire, il suffit de créer sur OnSerializing et de supprimer OnSerialized. Sur OnDeserializing, au lieu d’effacer, nous pourrions le libérer.

Je cherchais un problème similaire et j’ai trouvé ce post.

Voici un moyen de le faire en utilisant la reflection.

Pour le rendre plus générique, il faut vérifier le type de la propriété au lieu d’utiliser simplement ToSsortingng () dans propertyInfo.SetValue, à moins que toutes les propriétés réelles d’OFC soient des chaînes.

En outre, les noms de propriété en minuscules ne sont pas standard dans C #, mais étant donné que GetProperty est sensible à la casse, il existe peu d’autres options.

 public class Product { private Type _type; public Product() { fields = new Dictionary(); _type = GetType(); } public ssortingng id { get; set; } public ssortingng name { get; set; } public Dictionary fields { get; set; } public void SetProperty(ssortingng key, object value) { var propertyInfo = _type.GetProperty(key); if (null == propertyInfo) { fields.Add(key,value); return; } propertyInfo.SetValue(this, value.ToSsortingng()); } } ... private const ssortingng JsonTest = "{\"id\":7908,\"name\":\"product name\",\"_unknown_field_name_1\":\"some value\",\"_unknown_field_name_2\":\"some value\"}"; var product = new Product(); var data = JObject.Parse(JsonTest); foreach (var item in data) { product.SetProperty(item.Key, item.Value); }