Sérialiser les objets du Framework Entity dans JSON

il semble que la sérialisation des objets Entity Framework en JSON n'est pas possible en utilisant le Datacontractjserializer natif de la WCF ou ASP.NET le sérialiseur JavaScript natif. Cela est dû aux problèmes de comptage de référence que les deux sérialisateurs rejettent. J'ai aussi essayé Json.NET, qui échoue aussi spécifiquement sur une question de comptage de référence.


Edit: Json.NET peut maintenant sérialiser et désérialiser le cadre de L'entité les entités.


mes objets sont des objets Entity Framework, qui sont surchargés pour effectuer des fonctionnalités supplémentaires (par ex. l'authentification, etc.) et je ne veux pas de décorer ces classes avec la plate-forme d'attributs spécifiques, etc. comme je veux présenter une plateforme-API agnostique.

j'ai en fait blogué sur les étapes individuelles que je suis allé à https://blog.programx.co.uk/2009/03/18/wcf-json-serialization-woes-and-a-solution/

ai-je manqué quelque chose d'évident?

43
demandé sur Program.X 2009-03-18 14:53:49

8 réponses

la façon dont je fais ceci est en projetant les données que je veux sérialiser dans un type anonyme et sérialiser cela. Cela garantit que seulement l'information que je veux réellement dans le JSON est sérialisé, et je ne sérialise pas par inadvertance quelque chose plus loin dans le graphique d'objet. Il ressemble à ceci:

var records = from entity in context.Entities
              select new 
              {
                  Prop1 = entity.Prop1,
                  Prop2 = entity.Prop2,
                  ChildProp = entity.Child.Prop
              }
return Json(records);

je trouve les types anonymes à peu près idéaux pour cela. Le JSON, évidemment, ne se soucie pas de quel type a été utilisé pour le produire. Et les types anonymes vous donnent la flexibilité complète pour les propriétés et la structure que vous mettez dans le JSON.

71
répondu Craig Stuntz 2009-03-18 12:30:06

Microsoft a fait une erreur dans la façon dont ils ont transformé les objets EF en contrats de données. Ils incluaient les classes de base, et les liens arrières.

votre meilleur pari sera de créer des classes d'objets de transfert de données équivalentes pour chacune des entités que vous voulez retourner. Celles-ci comprendraient uniquement les données, et non le comportement, et non les parties D'une entité spécifiques à L'EE. Vous pouvez également créer des méthodes pour traduire vers et depuis vos classes DTO.

vos services retourneraient alors le Objets De Transfert De Données.

16
répondu John Saunders 2009-03-18 12:16:06

ma solution était simplement de supprimer la référence du parent sur les entités de mon enfant.

donc dans mon modèle, j'ai choisi la relation et j'ai changé la référence parentale pour être interne plutôt que Publique.

N'est peut-être pas la solution idéale pour tous, mais ça a marché pour moi.

2
répondu Anthony Main 2012-02-23 12:05:51

basé sur la réponse de @Craig Stuntz et similaire à un DTO, pour ma solution j'ai créé une classe partielle du modèle (dans un fichier séparé) et une méthode d'objet de retour avec comment je le veux en utilisant seulement les propriétés qui seront nécessaires.

namespace TestApplication.Models
{
    public partial class Employee
    {
        public object ToObject()
        {
            return new
            {
                 EmployeeID = EmployeeID,
                 Name = Name,
                 Username = Username,
                 Office = Office,
                 PhoneNumber = PhoneNumber,
                 EmailAddress = EmailAddress,
                 Title = Title,
                 Department = Department,
                 Manager = Manager
            };
        }
    }
}

Et puis je l'appelle tout simplement à mon retour:

var employee = dbCtx.Employees.Where(x => x.Name == usersName).Single();
return employee.ToObject();

je pense que la réponse acceptée est plus rapide et facile, j'utilise juste ma méthode pour garder toutes mes déclarations cohérentes et sèches.

2
répondu Pat Migliaccio 2016-05-10 17:50:32

une solution de plus si vous voulez avoir une meilleure cohérence de code est D'utiliser JavaScriptConverter qui traitera des dépendances de référence circulaire et ne sérialisera pas de telles références.

j'ai blogué sur ici:

http://hellowebapps.com/2010-09-26/producing-json-from-entity-framework-4-0-generated-classes/

1
répondu Mehal 2010-09-26 14:53:35

pour information j'ai trouvé une solution alternative

vous pouvez définir la relation mère comme privée de façon à ce que les propriétés ne soient pas exposées pendant la traduction en supprimant la boucle de propriété infinie

1
répondu Anthony Main 2012-05-28 10:10:32

je me suis bien battu avec ce problème pendant des jours,

la Solution. Dans votre fenêtre edmx. - clic droit et Ajouter un élément de génération de code - Sélectionnez l'onglet Code - sélectionnez EF 4x.Pococ Entity Generator

Si vous ne le voyez pas, alors vous devrez l'installer avec nuget, recherche EF.

le générateur D'Entity générera tout votre type complexe et l'objet entity dans des classes simples pour sérialiser dans json.

1
répondu Tebza 2012-07-24 13:35:01

Je l'ai résolu en obtenant seulement les types d'objet de l'espace de noms de système, et puis les convertir en Dictionnaire et puis les ajouter à la liste. Fonctionne bien pour moi :)

cela semble compliqué, mais c'était la seule solution générique qui fonctionnait pour moi... J'utilise cette logique pour un assistant que je fais, donc c'est pour une utilisation spéciale où j'ai besoin d'être capable d'intercepter chaque type d'objet dans un objet entity, peut-être que quelqu'un pourrait l'adapter à son utilisation.

List<Dictionary<string, string>> outputData = new List<Dictionary<string, string>>();

// convert all items to objects
var data = Data.ToArray().Cast<object>().ToArray();

// get info about objects; and get only those we need
// this will remove circular references and other stuff we don't need
PropertyInfo[] objInfos = data[0].GetType().GetProperties();
foreach (PropertyInfo info in objInfos) {
    switch (info.PropertyType.Namespace)
    { 
          // all types that are in "System" namespace should be OK
          case "System":
              propeties.Add(info.Name);
              break;
     }
}
Dictionary<string, string> rowsData = null;
foreach (object obj in data) {
     rowsData = new Dictionary<string, string>();
     Type objType = obj.GetType();
     foreach (string propertyName in propeties)
     {
//if You don't need to intercept every object type You could just call .ToString(), and remove other code
         PropertyInfo info = objType.GetProperty(propertyName);
         switch(info.PropertyType.FullName)
         {
               case "System.String":
                    var colData = info.GetValue(obj, null);
                    rowsData.Add(propertyName, colData != null ? colData.ToString() : String.Empty);
                    break;
//here You can add more variable types if you need so (like int and so on...)
           }
      }

      outputData .Add(rowsData); // add a new row
}

"outputData" est sûr pour JSON encode... J'espère que quelqu'un trouvera cette solution utile. C'était drôle de l'écrire :)

1
répondu Tom 2014-01-03 13:02:03