web-dev-qa-db-fra.com

Sérialiser des objets Entity Framework en JSON

Il semble que la sérialisation des objets Entity Framework en JSON n'est pas possible à l'aide du DataContractJsonSerializer natif de WCF ou du sérialiseur JavaScript natif d'ASP.NET. Cela est dû aux problèmes de comptage des références rejetés par les deux sérialiseurs. J'ai également essayé Json.NET , qui échoue également spécifiquement sur un problème de comptage de référence.


Modifier: Json.NET peut désormais sérialiser et désérialiser les entités Entity Framework .


Mes objets sont des objets Entity Framework, qui sont surchargés pour effectuer des fonctionnalités métier supplémentaires (par exemple, l'authentification, etc.) et je ne veux pas décorer ces classes avec des attributs spécifiques à la plate-forme, etc. car je veux présenter une API indépendante de la plate-forme .

J'ai en fait blogué sur les différentes étapes que j'ai suivies https://blog.programx.co.uk/2009/03/18/wcf-json-serialization-woes-and-a-solution/

Ai-je raté quelque chose d'évident?

45
Program.X

La façon dont je le fais est de projeter les données que je veux sérialiser en un type anonyme et de les sérialiser. Cela garantit que seules les informations que je veux réellement dans le JSON sont sérialisées, et je ne sérialise pas par inadvertance quelque chose plus bas dans le graphique d'objet. Cela 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 des types anonymes à peu près idéaux pour cela. Le JSON, évidemment, ne se soucie pas du type utilisé pour le produire. Et les types anonymes vous offrent une flexibilité totale quant aux propriétés et à la structure que vous mettez dans le JSON.

72
Craig Stuntz

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

Votre meilleur pari sera de créer des classes d'objets de transfert de données équivalentes pour chacune des entités que vous souhaitez retourner. Celles-ci incluraient uniquement les données, pas le comportement, et pas les parties spécifiques à EF d'une entité. Vous devez également créer des méthodes pour traduire vers et depuis vos classes DTO.

Vos services renverraient alors les objets de transfert de données.

16
John Saunders

Basé sur la réponse @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 la façon dont je le veux en utilisant uniquement 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 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 simplement ma méthode pour garder tous mes retours cohérents et SECS.

2
Pat Migliaccio

Ma solution était de simplement supprimer la référence parent sur mes entités enfants.

Donc, dans mon modèle, j'ai sélectionné la relation et changé la référence parent pour qu'elle soit interne plutôt que publique.

Peut ne pas être une solution idéale pour tous, mais a fonctionné pour moi.

2
Anthony Main

Une autre solution si vous souhaitez avoir une meilleure cohérence du code consiste à utiliser JavaScriptConverter qui gérera les dépendances des références circulaires et ne sérialisera pas ces références.

J'ai blogué ici:

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

1
Mehal

Je l'ai résolu en obtenant uniquement les types d'objets de l'espace de noms System, puis en les convertissant en Dictionnaire et en les ajoutant à la liste. Fonctionne bien pour moi :)

Cela semble compliqué, mais c'était la seule solution générique qui a fonctionné pour moi ... J'utilise cette logique pour un assistant que je crée, donc c'est pour une utilisation spéciale où j'ai besoin de pouvoir intercepter tous les types d'objet dans objet entité, 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 le codage JSON ... J'espère que quelqu'un trouvera cette solution utile. C'était amusant de l'écrire :)

1
Tom

Pour info j'ai trouvé une solution alternative

Vous pouvez définir la relation parent comme privée afin que les propriétés ne soient pas exposées pendant la traduction en supprimant la boucle de propriété infinie

1
Anthony Main

J'ai lutté avec ce problème pendant des jours,

Solution. À l'intérieur de votre fenêtre edmx. - faites un clic droit et ajoutez 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, vous devrez l'installer avec nuget, recherchez EF.

Le générateur d'entité générera tout votre type complexe et objet d'entité en classes simples à sérialiser en json.

1
Tebza