Casting interfaces pour la désérialisation de JSON.NET
Je suis en train de mettre en place un lecteur qui se tiendra dans les objets JSON à partir de différents sites web (pensez à l'information de grattage) et de les traduire en objets C#. Je suis actuellement en utilisant JSON.NET pour le processus de désérialisation. Le problème, je suis en cours d'exécution, c'est qu'il ne sait pas comment gérer l'interface des propriétés de niveau 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ée.
Il est relativement important d'avoir un IThingy par opposition à un Truc depuis le code, je suis en train de travailler sur sont considérées comme sensibles et les tests unitaires est très important. Se moquant des objets atomique des scripts de test n'est pas possible à part entière des objets comme Truc. Ils doivent être d'une interface.
J'ai été penché sur JSON.NET la documentation pour un certain temps maintenant, et les questions que j'ai pu trouver sur ce site ce sont tous de plus d'un an. Toute aide?
Aussi, si il le faut, mon appli est écrit .NET 4.0.
- double possible de à l'Aide de Json.NET convertisseurs de désérialiser des propriétés
Vous devez vous connecter pour publier un commentaire.
@SamualDavis fourni une excellente solution dans un une question relative à la, que je vais résumer ici.
Si vous avez désérialiser un flux JSON dans une classe concrète qui a les propriétés de l'interface, vous pouvez inclure les classes concrètes en tant que paramètres à un constructeur de la classe! Le NewtonSoft deserializer est assez intelligent pour comprendre qu'il doit utiliser ces classes concrètes pour désérialiser les propriétés.
Voici un exemple:
[JsonConstructor]
attribut.(Copié à partir de cette question)
Dans le cas où je n'ai pas eu de contrôle sur les entrants JSON (et ne peut donc pas s'assurer qu'il comprend un $type de propriété) j'ai écrit un convertisseur personnalisé qui permet de spécifier explicitement le type de béton:
Ce n'utilise la valeur par défaut processus de mise en œuvre de Json.Net tout en spécifiant explicitement le type de béton.
Une vue d'ensemble sont disponibles sur ce blog. Source code est ci-dessous:
ConcreteListTypeConverter<TInterface, TImplementation>
pour gérer les membres de la classe de typeIList<TInterface>
.concreteTypeConverter
dans la question.ConcreteListTypeConverter<TInterface, TImplementation>
mise en œuvre?Pourquoi utiliser un convertisseur? Il y a une fonctionnalité native dans
Newtonsoft.Json
pour résoudre ce problème exact:Ensemble
TypeNameHandling
dans leJsonSerializerSettings
àTypeNameHandling.Auto
Mets de chaque type dans le json, qui n'est pas considéré comme un béton instance d'un type, mais comme une interface ou une classe abstraite.
Je l'ai testé et il fonctionne comme un charme, même avec des listes.
Source et un autre manuel de mise en œuvre: Code À L'Intérieur De Blog
Objects
etAuto
tho?Pour activer la désérialisation de plusieurs implémentations d'interfaces, vous pouvez utiliser JsonConverter, mais pas par le biais d'un attribut:
DTOJsonConverter cartes de chaque interface avec une mise en œuvre concrète:
DTOJsonConverter est nécessaire uniquement pour les deserializer. Le processus de sérialisation est inchangé. L'objet Json n'avez pas besoin d'intégrer des types de béton noms.
Ce DONC, après les propose la même solution une étape plus loin avec un générique JsonConverter.
FullName
s lorsque vous pouvez simplement comparer directement les types?L'utilisation de cette classe, pour la cartographie de type abstrait de type réel:
...et quand désérialiser:
where TReal : TAbstract
pour s'assurer qu'il peut lancer le typewhere TReal : class, TAbstract, new()
.J'ai trouvé cela utile. Vous pourriez aussi.
Exemple D'Utilisation
De Création Personnalisé Convertisseur
Json.NET la documentation
Deux choses que vous pourriez essayer:
Mettre en œuvre un try/parse modèle:
Ou, si vous pouvez le faire dans votre modèle d'objet, de mettre en œuvre un béton de classe de base entre IPerson et votre feuille d'objets, et désérialiser à elle.
Le premier peut éventuellement échouer au moment de l'exécution, la seconde nécessite des modifications à votre modèle d'objet et homogénéise la sortie vers le plus petit dénominateur commun.
Pour ceux qui pourraient être curieux de connaître les ConcreteListTypeConverter qui a été référencé par Oliver, voici ma tentative:
CanConvert(Type objectType) { return true;}
. Il semble hacky, comment exactement est-ce utile? J'ai peut-être tort, mais ce n'est pas que l'envie de raconter une petite inexpérimenté de chasse qu'ils vont gagner le combat peu importe l'adversaire?Nicolas Westby a fourni une excellente solution dans un article génial.
Si vous voulez de la Désérialisation JSON à l'une des nombreuses classes qui implémentent une interface de ce genre:
Vous pouvez utiliser un JSON personnalisé converter:
Et vous aurez besoin pour décorer la "Profession" de la propriété avec un JsonConverter attribut de le faire savoir à l'utilisation de votre convertisseur personnalisé:
Et ensuite, vous pouvez lancer votre classe avec une Interface:
Supposons qu'un autofac réglage comme suit:
Ensuite, supposons que votre classe est comme ceci:
Par conséquent, l'utilisation de l'outil de résolution dans la désérialisation pourrait être:
Vous pouvez voir plus de détails dans http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm
Pour ce que ça vaut, j'ai fini par avoir à gérer de cette me pour la plupart. Chaque objet a une Deserialize(string jsonStream) méthode. Quelques extraits de celui-ci:
Dans ce cas, nouveau Truc(string) est un constructeur qui fera appel à la Deserialize(string jsonStream) méthode du type de béton. Ce régime va continuer à aller vers le bas et vers le bas jusqu'à arriver à la base de points que json.NET pouvez simplement gérer.
Ainsi de suite et ainsi de suite. Cette configuration m'a permis de donner json.NET des configurations, il peut gérer sans avoir à refactoriser une grande partie de la bibliothèque elle-même ou à l'aide de lourdes essayer/analyser des modèles qui s'enlisent l'ensemble de notre bibliothèque en raison du nombre d'objets impliqués. Cela signifie aussi que je peux gérer tout json changements sur un objet spécifique, et je n'ai pas besoin de vous inquiéter de tout ce que l'objet touche. Ce n'est pas la solution idéale, mais il fonctionne très bien à partir de notre unité et de tests d'intégration.
Plusieurs années et j'ai eu un problème similaire. Dans mon cas n'ont été fortement imbriqués les interfaces et une préférence pour générer les classes concrètes au moment de l'exécution, de sorte qu'Il serait de travailler 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 renvoyé par Newtonsoft.
L'avantage de cette approche est qu'il ne nécessite pas une mise en œuvre concrète de la classe et peut gérer n'importe quelle profondeur d'imbrication des interfaces automatiquement. Vous pouvez en savoir plus à ce sujet sur mon blog.
Utilisation:
PopulateObject
sur le proxy généré par l'Impromptu de l'Interface. Je n'ai malheureusement abandonné aller pour le Duck-Typing - c'était plus facile de créer un personnalisé Json Contrat Sérialiseur que l'habitude de la réflexion afin de trouver une implémentation de l'interface demandée et en les utilisant.Aucun objet ne jamais être un IThingy que les interfaces sont toutes, par définition, abstrait.
L'objet que vous avez qui a d'abord été sérialisé est béton type, la mise en œuvre de la résumé interface. Vous avez besoin d'avoir cette même béton classe de relancer les données sérialisées.
L'objet résultant sera alors de quelque type que implémente la résumé interface vous êtes à la recherche pour.
De la la documentation il s'ensuit que vous pouvez utiliser
lors de la désérialisation d'informer JSON.NET sur le type de béton.
_type
propriété qui signale le type de béton à utiliser.Type
pour relancer l'objet avec._type
allusion... Est-il si difficile de faire comprendre? Si vous avez le nom de l'interface dans le JSON, puis ont afunction qui peut l'inspecter et le retour de laType
de sorte qu'il peut être utilisé parDeserializeObject
. Dans l'ensemble, il y a plusieurs façons de le faire - c'est loin d'être difficile.property
d'un objet, qui est de typeIThingy
? Si c'est le cas, alors votre question doit refléter ce que c'est complètement différent de la matière.. Dans ce cas, la chose la plus sûre serait de faire juste un/s/IThingy/Thingy
sur le JSON pour remplacer les interfaces avec des implémentations concrètes.._type
soupçon est le résoudre pour vous, et vous semblez être attendre un peu de magie interrupteur qui fera tout pour vous. Une option pourrait être pour décorer le contrat principal avec allusion attributs, quelque chose comme[ConcreateImpl(typeof(Thingy)] Public IThingy Thingy;
. Mais: Vous avez un sérieux problème ici - vous que vous disposez de plusieurs types d'objets et de multiples sources, vous n'avez pas le contrôle, et donc aucun moyen d'identifier quel type abstrait n'importe quel morceau de JSON se réfère à... cela signifie que vous ne pouvez pas faire cela, c'est par définition impossible.{"__type":"ConsoleApplication1.Person, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
Ma solution à celui-ci, je l'aime, car il est bien général, est comme suit:
}
Vous pourriez évidemment, et trivialement convertir une convertisseur universel par l'ajout d'un constructeur qui a eu un argument de type Dictionary<, Type> avec pour instancier les conversions variable d'instance.
Ma solution a été ajouté les éléments de l'interface dans le constructeur.