Comment puis-je faire JSON.NET ignorer les relations d'objet?
Je suis en train de travailler sur un Entity Framework projet. Je veux sérialiser un tas de l'entité des instances de classe. J'ai lié ces ensemble dans une classe de conteneur:
public class Pseudocontext
{
public List<Widget> widgets;
public List<Thing> things;
Etc... c'est une instance de cette classe que je suis en tentant de sérialiser. Je veux JSON.NET pour sérialiser les membres de chaque entité instance de classe qui sont en fait des colonnes dans la base de données. Je ne veux pas qu'il tente même de sérialiser les références de l'objet.
En particulier, les classes d'entité ont des membres virtuels qui me permettent d'écrire du code C# qui permet de naviguer tous mes inter-entités, relations, sans se soucier de réelles valeurs de clé, joint, etc., et je veux JSON.NET pour ignorer les parties associées de mes classes d'entité.
Sur la surface, il semble y avoir un JSON.NET option de configuration qui fait exactement de quoi je parle:
JsonSerializer serializer = new JsonSerializer();
serializer.PreserveReferencesHandling = PreserveReferencesHandling.None;
Malheureusement, JSON.NET semble ignorer le deuxième énoncé ci-dessus.
J'ai effectivement trouvé une page web (http://json.codeplex.com/workitem/24608) où quelqu'un d'autre a le même problème à l'attention de James Newton-King lui-même, et sa réponse (dans son intégralité) a été "Rédiger un contrat personnalisé de résolution."
Aussi inadéquate que je trouve que la réponse, j'ai été de tenter de suivre ses conseils. J'aimerais beaucoup être en mesure de rédiger un "contrat de résolution", qui ignorait tout, sauf les types primitifs, des cordes, des objets DateTime, et de ma propre Pseudocontext classe avec les Listes qu'il contient directement. Si quelqu'un a un exemple de quelque chose qui, au moins, ressemble à cela, il peut être tout ce dont j'ai besoin. C'est ce que j'ai trouvé sur mon propre:
public class WhatDecadeIsItAgain : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
JsonContract contract = base.CreateContract(objectType);
if (objectType.IsPrimitive || objectType == typeof(DateTime) || objectType == typeof(string)
|| objectType == typeof(Pseudocontext) || objectType.Name.Contains("List"))
{
contract.Converter = base.CreateContract(objectType).Converter;
}
else
{
contract.Converter = myDefaultConverter;
}
return contract;
}
private static GeeThisSureTakesALotOfClassesConverter myDefaultConverter = new GeeThisSureTakesALotOfClassesConverter();
}
public class GeeThisSureTakesALotOfClassesConverter : Newtonsoft.Json.Converters.CustomCreationConverter<object>
{
public override object Create(Type objectType)
{
return null;
}
}
Quand je tente d'utiliser le haut (par la mise en sérialiseur.ContractResolver à une instance de WhatDecadeIsItAgain avant de sérialisation), j'obtiens des erreurs de dépassement de mémoire lors de la sérialisation qui indiquent que JSON.NET rencontre des boucles de référence que ne jamais mettre fin (en dépit de mes efforts pour faire JSON.NET simplement ignorer les références de l'objet).
Je pense que mon "contrat personnalisé résolution" peut-être tort. Comme indiqué ci-dessus, il est construit autour du principe que je doit retourner la valeur par défaut de "contrat" pour les types, je ne veux sérialiser, et un "contrat" qui retourne "null" pour tous les autres types.
Je n'ai aucune idée de comment corriger ces hypothèses sont, cependant, et il n'est pas facile à raconter. L'JSON.NET le design est très largement basé sur la mise en œuvre de l'héritage, redéfinition de méthode, etc.; Je ne suis pas beaucoup d'une La programmation orientée objet gars, et je trouve que ce genre de conception assez obscure. Ont-il un "contrat personnalisé résolveur" de l'interface que j'ai pu mettre en œuvre, Visual Studio 2012 serait capable d'écraser les méthodes requises très rapidement, et j'imagine que j'aurais peu de mal à remplir les talons avec une logique réelle.
J'aurais pas de problème d'écriture, par exemple, une méthode qui renvoie "vrai" si je veux sérialiser un objet de fourni et de type "false" sinon. Je suis peut-être raté quelque chose, mais j'ai pas trouvé la méthode pour remplacer, et je n'ai pas été en mesure de trouver l'hypothétique interface (ICustomContractResolver ?) qui serait me dire ce que je suis censé faire dans le dernier extrait de code inséré ci-dessus.
Aussi, je me rends compte qu'il y a JSON.NET attributs ([JsonIgnore]?) qui sont conçus pour faire face à des situations de ce genre. Je ne peux pas vraiment utiliser cette approche, depuis que je suis en utilisant un modèle "d'abord". Sauf si je décide de déchirer l'ensemble de mon projet d'architecture, de mes classes d'entité sont générés automatiquement, ils ne contiennent JsonIgnore attributs, ni ce que je me sens à l'aise d'édition automatisée de classes pour contenir ces attributs.
D'ailleurs, pendant un temps, je ne les choses ont-elles mis en place pour sérialiser les références de l'objet, et j'étais juste en ignorant tout le superflu "ref $" et "$id" données JSON.NET était de retour dans sa sérialisation de sortie. J'ai abandonné cette approche, pour le moment du moins, parce que (précipitamment) sérialisation commencé à prendre une quantité excessive de temps (~45 minutes pour arriver ~5 MO de JSON).
Je n'ai pas été en mesure de lier ce soudain changement dans la performance de retour à quelque chose de spécifique que j'ai fait. Si quoi que ce soit, le volume de données dans ma base de données est plus bas aujourd'hui qu'il ne l'était lors de la sérialisation était réellement terminer dans un délai raisonnable. Mais je serais plus qu'heureux, avec un retour à la statu quo ante (dans lequel j'étais juste avoir à ignorer "ref$", "$id", etc.) si cet objectif pouvait être atteint.
À ce point, je suis aussi ouvert à la perspective d'utiliser une autre librairie JSON, ou d'une stratégie différente, tout à fait. Je sens que je pourrais simplement utiliser StringBuilder, Système.Réflexion, etc. et de venir avec ma propre solution maison... mais n'est-ce pas JSON.NET censé être en mesure de gérer ce genre de chose assez facilement??
Vous devez vous connecter pour publier un commentaire.
Tout d'abord, pour répondre à vos questions avec les boucles de référence-- Le
PreserveReferencesHandling
paramètre contrôle si Json.Net émet$id
et$ref
de piste inter-références de l'objet. Si vous possédez ce jeu deNone
et votre objet graphique contient des boucles, alors vous aurez également besoin de mettreReferenceLoopHandling
àIgnore
pour éviter les erreurs.Maintenant, pour obtenir Json.Net pour ignorer toutes les références d'objets tout à fait et ne sérialiser primitives propriétés (sauf dans votre
Pseudocontext
classe de cours), vous avez besoin d'un Contrat personnalisé de résolution, comme vous l'avez suggéré. Mais ne vous inquiétez pas, il n'est pas aussi difficile que vous le pensez. Le résolveur a la capacité d'injecter unShouldSerialize
méthode pour chaque propriété contrôle si oui ou non cette propriété doit être inclus dans la sortie. Donc, tout ce que vous devez faire est de tirer votre résolveur de celui par défaut, puis remplacer leCreateProperty
méthode telle qu'elle fixeShouldSerialize
de façon appropriée. (Vous n'avez pas besoin d'un customJsonConverter
ici, mais il est possible de résoudre ce problème avec cette approche. Il aurait besoin d'un peu plus de code, cependant.)Voici le code pour le résolveur:
Voici une démo montrant que le programme de résolution dans l'action.
De sortie:
Espère que c'est dans le stade de ce que vous recherchez.
Aussi, si vous êtes à la recherche pour une manière de le faire pour l'ensemble de votre modèle de classes avec différents type de membre (les nomspar exemple, vous avez quelques modèles créés par Entity Framework) cette réponse peut vous aider et vous pouvez ignorer les propriétés de navigation dans la sérialisation JSON par elle.
Une méthode plus simple est de modifier votre modèle de modèle T4 (.tt) pour ajouter
[JsonIgnore]
attributs de vos propriétés de navigation, qui seront il suffit de laisser les types primitifs comme sérialisable.