J'essaie d'étendre l'exemple JSON.net donné ici http://james.newtonking.com/projects/json/help/CustomCreationConverter.html
J'ai une autre sous-classe dérivée de la classe de base / interface
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class Artist : Person
{
public string Skill { get; set; }
}
List<Person> people = new List<Person>
{
new Employee(),
new Employee(),
new Artist(),
};
Comment désérialiser après Json retour à la liste <Personne>
[
{
"Department": "Department1",
"JobTitle": "JobTitle1",
"FirstName": "FirstName1",
"LastName": "LastName1"
},
{
"Department": "Department2",
"JobTitle": "JobTitle2",
"FirstName": "FirstName2",
"LastName": "LastName2"
},
{
"Skill": "Painter",
"FirstName": "FirstName3",
"LastName": "LastName3"
}
]
Je ne veux pas utiliser TypeNameHandling JsonSerializerSettings. Je recherche spécifiquement une implémentation JsonConverter personnalisée pour gérer cela. La documentation et les exemples à ce sujet sont assez rares sur le net. Je n'arrive pas à obtenir l'implémentation de la méthode ReadJson () substituée dans JsonConverter à droite.
c#
json
json.net
deserialization
Snakebyte
la source
la source
Réponses:
En utilisant la norme
CustomCreationConverter
, j'avais du mal à travailler comment générer le type correct (Person
ouEmployee
), car pour le déterminer, vous devez analyser le JSON et il n'y a aucun moyen intégré de le faire à l'aide de laCreate
méthode.J'ai trouvé un fil de discussion concernant la conversion de type et il s'est avéré fournir la réponse. Voici un lien: Conversion de type .
Ce qui est nécessaire est de sous
JsonConverter
-classer, de remplacer laReadJson
méthode et de créer une nouvelleCreate
méthode abstraite qui accepte aJObject
.La
ReadJson
méthode substituée crée unJObject
et invoque laCreate
méthode (implémentée par notre classe de convertisseur dérivée), en passant l'JObject
instance.Cette
JObject
instance peut ensuite être analysée pour déterminer le type correct en vérifiant l'existence de certains champs.Exemple
la source
JsonReader
créé dans laReadJson
méthode ne possèdes pas des valeurs de configuration du lecteur d' origine (Culture
,DateParseHandling
,DateTimeZoneHandling
,FloatParseHandling
, etc ...). Ces valeurs doivent être copiées avant d'utiliser le nouveauJsonReader
dansserializer.Populate()
.JsonConverter
a une propriété appeléeCanRead
etCanWrite
. Si vous n'avez pas besoin d'uneWriteJson
implémentation personnalisée , il suffit de laisserCanWrite
revenirFALSE
. Le système reviendra alors au comportement par défaut. @jdavies: veuillez ajouter cela à votre réponse. Sinon, il se bloquera lors de la sérialisation.La solution ci-dessus pour le
JsonCreationConverter<T>
est partout sur Internet, mais a un défaut qui se manifeste en de rares occasions. Le nouveau JsonReader créé dans la méthode ReadJson n'hérite d'aucune des valeurs de configuration du lecteur d'origine (Culture, DateParseHandling, DateTimeZoneHandling, FloatParseHandling, etc ...). Ces valeurs doivent être copiées avant d'utiliser le nouveau JsonReader dans serializer.Populate ().C'est le mieux que j'ai pu trouver pour résoudre certains des problèmes liés à la mise en œuvre ci-dessus, mais je pense toujours que certaines choses sont négligées:
Mise à jour J'ai mis à jour ceci pour avoir une méthode plus explicite qui fait une copie d'un lecteur existant. Cela résume simplement le processus de copie des paramètres JsonReader individuels. Idéalement, cette fonction serait conservée dans la bibliothèque Newtonsoft elle-même, mais pour l'instant, vous pouvez utiliser ce qui suit:
Cela devrait être utilisé comme suit:
Une solution plus ancienne suit:
la source
Je pensais simplement partager une solution basée sur cela qui fonctionne avec l'attribut Knowntype à l'aide de la réflexion, devait obtenir une classe dérivée de n'importe quelle classe de base, la solution peut bénéficier de la récursivité pour trouver la meilleure classe correspondante même si je n'en avais pas besoin dans mon cas, la correspondance est effectuée par le type donné au convertisseur s'il a des KnownTypes, il les analysera tous jusqu'à ce qu'il corresponde à un type qui a toutes les propriétés à l'intérieur de la chaîne json, le premier à correspondre sera choisi.
l'utilisation est aussi simple que:
dans le cas ci-dessus, ret sera de type B.
Classes JSON:
Code convertisseur:
la source
Le projet JsonSubTypes implémente un convertisseur générique qui gère cette fonctionnalité à l'aide d'attributs.
Pour l'exemple concret fourni ici, voici comment cela fonctionne:
la source
Ceci est une extension de la réponse de totem. Il fait essentiellement la même chose mais la correspondance de propriété est basée sur l'objet json sérialisé, et non sur l'objet .net. Ceci est important si vous utilisez [JsonProperty], utilisez CamelCasePropertyNamesContractResolver ou faites quoi que ce soit d'autre qui empêcherait le json de correspondre à l'objet .net.
L'utilisation est simple:
Code convertisseur:
la source
Comme autre variante de la solution de type connu de Totem, vous pouvez utiliser la réflexion pour créer un résolveur de type générique pour éviter d'avoir à utiliser des attributs de type connus.
Cela utilise une technique similaire à GenericResolver de Juval Lowy pour WCF.
Tant que votre classe de base est abstraite ou une interface, les types connus seront automatiquement déterminés plutôt que d'avoir à être décorés avec des attributs de type connus.
Dans mon propre cas, j'ai choisi d'utiliser une propriété $ type pour désigner le type dans mon objet json plutôt que d'essayer de le déterminer à partir des propriétés, bien que vous puissiez emprunter à d'autres solutions ici pour utiliser la détermination basée sur les propriétés.
Il peut ensuite être installé en tant que formateur
la source
Voici une autre solution qui évite l'utilisation de
jObject.CreateReader()
, et en crée un nouveauJsonTextReader
(qui est le comportement utilisé par laJsonCreate.Deserialze
méthode par défaut :la source
La plupart du temps, l'implémentation existera dans le même espace de noms que l'interface. Donc, je suis venu avec ceci:
Par conséquent, vous pouvez l'inclure globalement comme ceci:
la source
En utilisant l'idée de totem et zlangner , j'ai créé un
KnownTypeConverter
qui sera en mesure de déterminer l'héritier le plus approprié, tout en tenant compte du fait que les données json peuvent ne pas avoir d'éléments facultatifs.Ainsi, le service envoie une réponse JSON qui contient un tableau de documents (entrants et sortants). Les documents ont à la fois un ensemble commun d'éléments et des éléments différents. Dans ce cas, les éléments liés aux documents sortants sont facultatifs et peuvent être absents.
À cet égard, une classe de base a
Document
été créée qui comprend un ensemble commun de propriétés. Deux classes héritières sont également créées: -OutgoingDocument
ajoute deux éléments facultatifs"device_id"
et"msg_id"
; -IncomingDocument
ajoute un élément obligatoire"sender_id"
;La tâche consistait à créer un convertisseur qui, sur la base des données et des informations json de KnownTypeAttribute, sera en mesure de déterminer la classe la plus appropriée qui vous permettra d'enregistrer la plus grande quantité d'informations reçues. Il convient également de tenir compte du fait que les données json peuvent ne pas avoir d'éléments facultatifs. Pour réduire le nombre de comparaisons des éléments json et des propriétés des modèles de données, j'ai décidé de ne pas prendre en compte les propriétés de la classe de base et de corréler avec les éléments json uniquement les propriétés des classes héritières.
Données du service:
Modèles de données:
Convertisseur:
PS: Dans mon cas, si aucun héritier n'a été sélectionné par le convertisseur (cela peut se produire si les données JSON contiennent uniquement des informations de la classe de base ou si les données JSON ne contiennent pas d'éléments facultatifs de la
OutgoingDocument
), alors un objet de laOutgoingDocument
classe sera créé, car il est répertorié en premier dans la liste desKnownTypeAttribute
attributs. À votre demande, vous pouvez varier la mise en œuvre de laKnownTypeConverter
dans cette situation.la source