J'essaie de configurer un lecteur qui prendra des objets JSON de divers sites Web (pensez à la récupération d'informations) et les traduira en objets C #. J'utilise actuellement JSON.NET pour le processus de désérialisation. Le problème que je rencontre est qu'il ne sait pas comment gérer les propriétés au niveau de l'interface dans une classe. Donc quelque chose de la nature:
public IThingy Thing
Produira l'erreur:
Impossible de créer une instance de type IThingy. Le type est une interface ou une classe abstraite et ne peut pas être instancié.
Il est relativement important que ce soit un IThingy plutôt qu'un Thingy car le code sur lequel je travaille est considéré comme sensible et les tests unitaires sont très importants. La simulation d'objets pour les scripts de test atomique n'est pas possible avec des objets à part entière comme Thingy. Ils doivent être une interface.
Je me penche sur la documentation de JSON.NET depuis un moment maintenant, et les questions que j'ai pu trouver sur ce site à ce sujet remontent toutes à plus d'un an. De l'aide?
De plus, si cela compte, mon application est écrite en .NET 4.0.
Réponses:
@SamualDavis a fourni une excellente solution dans une question connexe , que je résumerai ici.
Si vous devez désérialiser un flux JSON dans une classe concrète qui a des propriétés d'interface, vous pouvez inclure les classes concrètes en tant que paramètres d'un constructeur pour la classe! Le désérialiseur NewtonSoft est suffisamment intelligent pour comprendre qu'il doit utiliser ces classes concrètes pour désérialiser les propriétés.
Voici un exemple:
la source
[JsonConstructor]
attribut.(Copié de cette question )
Dans les cas où je n'ai pas eu de contrôle sur le JSON entrant (et ne peux donc pas m'assurer qu'il inclut une propriété $ type), j'ai écrit un convertisseur personnalisé qui vous permet simplement de spécifier explicitement le type concret:
Cela utilise simplement l'implémentation du sérialiseur par défaut de Json.Net tout en spécifiant explicitement le type concret.
Un aperçu est disponible sur ce billet de blog . Le code source est ci-dessous:
la source
ConcreteListTypeConverter<TInterface, TImplementation>
pour gérer les membres de classe de typeIList<TInterface>
.concreteTypeConverter
la question.ConcreteListTypeConverter<TInterface, TImplementation>
implémentation?Pourquoi utiliser un convertisseur? Il existe une fonctionnalité native
Newtonsoft.Json
pour résoudre ce problème exact:Situé
TypeNameHandling
dans leJsonSerializerSettings
àTypeNameHandling.Auto
Cela mettra chaque type dans le json, qui n'est pas considéré comme une instance concrète d'un type mais comme une interface ou une classe abstraite.
Assurez-vous que vous utilisez les mêmes paramètres pour la sérialisation et la désérialisation .
Je l'ai testé, et cela fonctionne à merveille, même avec des listes.
Résultats de la recherche Résultat Web avec liens vers des sites
⚠️ AVERTISSEMENT :
Utilisez-le uniquement pour json à partir d'une source connue et fiable. L'utilisateur snipsnipsnip a correctement mentionné qu'il s'agit en effet d'une vunerability.
Voir CA2328 et SCS0028 pour plus d'informations.
Source et une implémentation manuelle alternative: Code Inside Blog
la source
Pour activer la désérialisation de plusieurs implémentations d'interfaces, vous pouvez utiliser JsonConverter, mais pas via un attribut:
DTOJsonConverter mappe chaque interface avec une implémentation concrète:
DTOJsonConverter n'est requis que pour le désérialiseur. Le processus de sérialisation est inchangé. L'objet Json n'a pas besoin d'incorporer des noms de types concrets.
Ce poste SO offre la même solution un peu plus loin avec un JsonConverter générique.
la source
FullName
s alors que vous pouvez simplement comparer des types directement?Utilisez cette classe, pour mapper le type abstrait au type réel:
... et lors de la désérialisation:
la source
where TReal : TAbstract
pour vous assurer qu'il peut lancer le typewhere TReal : class, TAbstract, new()
.Nicholas Westby a fourni une excellente solution dans un article génial .
Si vous souhaitez désérialiser JSON vers l'une des nombreuses classes possibles qui implémentent une interface comme celle-ci:
Vous pouvez utiliser un convertisseur JSON personnalisé:
Et vous devrez décorer la propriété "Profession" avec un attribut JsonConverter pour lui faire savoir d'utiliser votre convertisseur personnalisé:
Et puis, vous pouvez diffuser votre classe avec une interface:
la source
Deux choses que vous pourriez essayer:
Implémentez un modèle try / parse:
Ou, si vous pouvez le faire dans votre modèle objet, implémentez une classe de base concrète entre IPerson et vos objets feuille, et désérialisez-la.
Le premier peut potentiellement échouer à l'exécution, le second nécessite des modifications de votre modèle d'objet et homogénéise la sortie au plus petit dénominateur commun.
la source
J'ai trouvé cela utile. Vous pourriez aussi.
Exemple d'utilisation
Convertisseur de création personnalisée
Documentation Json.NET
la source
Pour ceux qui pourraient être curieux de connaître le ConcreteListTypeConverter qui a été référencé par Oliver, voici ma tentative:
la source
CanConvert(Type objectType) { return true;}
. Cela semble piraté, en quoi cela est-il utile? Je me trompe peut-être, mais n'est-ce pas comme dire à un petit combattant inexpérimenté qu'il va gagner le combat, peu importe l'adversaire?Pour ce que ça vaut, j'ai fini par devoir gérer cela moi-même pour la plupart. Chaque objet a une méthode Deserialize (string jsonStream) . Quelques extraits de celui-ci:
Dans ce cas, new Thingy (string) est un constructeur qui appellera la méthode Deserialize (string jsonStream) du type concret approprié. Ce schéma continuera à descendre et à descendre jusqu'à ce que vous atteigniez les points de base que json.NET peut simplement gérer.
Etc., etc. Cette configuration m'a permis de donner des configurations json.NET qu'il peut gérer sans avoir à refactoriser une grande partie de la bibliothèque elle-même ou à utiliser des modèles try / parse peu maniables qui auraient embourbé toute notre bibliothèque en raison du nombre d'objets impliqués. Cela signifie également que je peux gérer efficacement toutes les modifications json sur un objet spécifique et que je n'ai pas besoin de m'inquiéter de tout ce que cet objet touche. Ce n'est en aucun cas la solution idéale, mais elle fonctionne assez bien à partir de nos tests unitaires et d'intégration.
la source
Supposons un paramètre autofac comme celui-ci:
Ensuite, supposons que votre classe ressemble à ceci:
Par conséquent, l'utilisation du résolveur dans la désérialisation pourrait être comme:
Vous pouvez voir plus de détails sur http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm
la source
Aucun objet ne jamais être un IThingy comme interfaces sont abstraites par définition.
L'objet que vous avez qui a été sérialisé pour la première fois était d'un type concret , implémentant l' interface abstraite . Vous devez avoir ce même béton classe raviver les données sérialisées.
L'objet résultant sera alors d'un type qui implémente l' interface abstraite que vous recherchez.
De la documentation, il suit que vous pouvez utiliser
lors de la désérialisation pour informer JSON.NET du type concret.
la source
_type
propriété qui indique le type de béton à utiliser.Ma solution à celle-ci, que j'aime car elle est bien générale, est la suivante:
}
Vous pouvez évidemment et trivialement le convertir en un convertisseur encore plus général en ajoutant un constructeur qui prend un argument de type Dictionary <Type, Type> avec lequel instancier la variable d'instance de conversions.
la source
Plusieurs années plus tard, j'ai eu un problème similaire. Dans mon cas, il y avait des interfaces fortement imbriquées et une préférence pour la génération des classes concrètes au moment de l'exécution afin que cela fonctionne avec une classe générique.
J'ai décidé de créer une classe proxy au moment de l'exécution qui encapsule l'objet retourné par Newtonsoft.
L'avantage de cette approche est qu'elle ne nécessite pas d'implémentation concrète de la classe et peut gérer automatiquement n'importe quelle profondeur d'interfaces imbriquées. Vous pouvez en voir plus sur mon blog .
Usage:
la source
PopulateObject
sur le proxy généré par Impromptu Interface. J'ai malheureusement abandonné Duck Typing - il était simplement plus facile de créer un sérialiseur de contrat Json personnalisé qui utilisait la réflexion pour trouver une implémentation existante de l'interface demandée et l'utiliser.Utilisez ce JsonKnownTypes , c'est une manière très similaire d'utiliser, il suffit d'ajouter un discriminateur à json:
Maintenant, lorsque vous sérialisez un objet dans json sera ajouté
"$type"
avec"myClass"
valeur et il sera utilisé pour désérialiserJson:
la source
Ma solution a été ajoutée les éléments d'interface dans le constructeur.
la source