échec de la sérialisation de la réponse dans l'API Web

86

Je travaillais sur l'API Web ASP.NET MVC, j'ai cette erreur:

Le type 'ObjectContent`1' n'a pas réussi à sérialiser le corps de la réponse pour le type de contenu 'application / xml; jeu de caractères = utf-8 '.

Mon contrôleur est:

public Employee GetEmployees()
{
    Employee employees = db.Employees.First();
    return employees;
}

pourquoi j'obtiens cette erreur?

Tamal kanti Dey
la source
6
L'exception que vous voyez est une exception générale, qui peut être causée par un certain nombre de facteurs. Vérifiez la InnerExceptionpropriété de l'exception de sérialisation pour savoir ce qui a exactement provoqué l'échec de la sérialisation.
Afficher le nom du
Pouvez-vous partager le code de votre type d'employé? C'est peut-être parce que le type Employee n'est pas sérialisable ...
Maggie Ying
Jetez également un œil à ce stackoverflow.com/questions/8173524/…
sttaq
Double
NH.

Réponses:

121

Pour moi, c'était un problème avec le référencement circulaire.

La réponse acceptée n'a pas fonctionné pour moi car elle ne modifie que le comportement du formateur JSON, mais j'obtenais du XML lorsque j'ai appelé le service depuis le navigateur.

Pour résoudre ce problème, j'ai désactivé XML et forcé uniquement le retour de JSON.

Dans le fichier Global.asax, placez les lignes suivantes en haut de votre méthode Application_Start:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

Désormais, seuls les résultats JSON seront renvoyés. Si vous avez besoin de résultats XML, vous devrez trouver une solution différente.

Zane
la source
a travaillé pour moi, mais le fait est que j'utilise POSTMAN. c'est une extension chrome. lorsque je poste des données avec POSTMAN, cela fonctionne très bien. mais quand j'utilise restsharp, cela me donne cette erreur. de toute façon votre solution a résolu mon problème
ArgeKumandan
Cette réponse ne fournit pas de solution pour utiliser xml, et c'est ce qu'il a demandé.
Honestduane
Fonctionne pour moi car je suis passé à json à partir de xml.
Sike12
En fait, cette réponse va à la racine du problème. La première erreur que j'ai reçue était une erreur de référence circulaire (en essayant de renvoyer JSON à partir du contrôleur MVC). Lorsque je suis passé à un contrôleur hérité de l'API, j'ai commencé à obtenir cette erreur à la place. Lorsque j'ai ajouté le code ci-dessus à Global.asax, l'erreur a disparu.
Matthew Pitts
43

dans votre fichier global.asax, dans la méthode Application_start () ajoutez cette ligne:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

J'espère que cela vous aide!

Julito Avellaneda
la source
3
Qu'est-ce que application_start et où le trouver? Et où exactement cette ligne doit-elle être placée?
Ciaran Gallagher
4
Désolé mais quelle est la signification de cette déclaration?
Blaise
5
Ligne ajoutée à celle Global.asaxde Application_Start, mais pas de changement.
cheesus
8
Cela ne fonctionnait toujours pas pour moi. Cependant, j'ai ajouté une autre ligne après celle de cette réponse et cela a fonctionné: GlobalConfiguration.Configuration.Formatters.Remove (GlobalConfiguration.Configuration.Formatters.XmlFormatter); J'ai créé une réponse plus complète ci
Zane
2
Cette réponse ne doit pas être acceptée car elle supprime en effet XmlSerializer plutôt que de résoudre le problème de la référence circulaire avec XmlSerializer.
Believe2014
29

J'ai le même problème. Et je l'ai résolu. J'ai mis le constructeur par défaut dans la classe DTO.

Ex:

public class User
{
    public User()
    {
    }
}

J'espère que cela fonctionnera avec vous!

taynguyen
la source
Merci pour la suggestion, cela m'a aidé avec la réponse xml, mais est-ce que quelqu'un sait pourquoi il a besoin d'un constructeur par défaut? Nous avons déjà les données ...
Ilya Chernomordik
Je pense que lorsqu'un objet est sérialisé à partir de la réponse, le constructeur a d'abord été appelé pour créer une instance d'objet, après cela, la méthode set est utilisée pour définir les données sur l'instance d'objet. C'est ma supposition.
taynguyen
Cela devrait en fait être la réponse choisie car elle ne fait aucune hypothèse sur les types de retour dont vous avez besoin. Cela fonctionnera à la fois pour XML et JSON. Merci d'avoir publié ceci.
Allen Underwood
22

Mettez ceci dans le constructeur. J'espère que cela résoudra le problème:

    public MyController()
    {

        db.Configuration.ProxyCreationEnabled = false;
    }
Sadjad Khazaie
la source
Excellente solution. J'ai besoin de le mettre dans le constructeur et cela a fonctionné.
InsParbo
Cela a fonctionné pour moi en combinaison avec les paramètres GlobalConfiguration. Mais pourquoi ça marche? Une explication sur la façon dont cela résout le problème? Et quel était le problème?
Ciaran Gallagher
Pour comprendre ce que sont les proxys d'entité: msdn.microsoft.com/en-us/library/jj592886(v=vs.113).aspx Pour comprendre ce qu'est ProxyCreationEnabled: stackoverflow.com/questions/7111109/…
Sadjad Khazaie
16

J'ai trouvé deux solutions à cela. Le premier et le plus simple à implémenter est de changer les IEnumerables, ICollections en un type de List. WebAPI peut sérialiser ces objets, mais ne peut pas sérialiser les types d'interface.

public class Store
{

  [StringLength(5)]
    public string Zip5 { get; set; }

    public virtual List<StoreReport> StoreReports { get; set; }  //use a list here
 }

L'autre option consiste à ne pas utiliser le sérialiseur JSON natif et à exécuter ce remplacement dans la méthode Register de WebApi Config:

        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
        config.Formatters.Remove(config.Formatters.XmlFormatter);
Ray Suelzer
la source
1
Tout en passant à List a fonctionné pour moi, l'ajout d'un constructeur sans paramètre a également fonctionné pour moi et je pouvais continuer à renvoyer IEnumerable <Widget>
Mike Cheel
8

La solution est simple.

Après la requête LINQ, ajoutez .ToList () (ou ToDictionary si besoin).

Cela fera un chargement impatient que le chargement paresseux des données

om471987
la source
1
Changer le type de retour d'action IENumerableet l'ajouter .TiList()au retour a fonctionné pour moi.
Ricardo Souza
5

** ce bogue se produit lors de l'appel à partir de la requête web api / wcf / ... du côté client, mais comme effet secondaire, vous devrez inclure les relations dépendantes par le mot clé include. **

public CustomerPortalContext()
            : base("Name=CustomerPortalContext")
        {
            base.Configuration.ProxyCreationEnabled = false;
        }
Mohamed.Abdo
la source
4

Si vous travaillez avec EF, en plus d'ajouter le code ci-dessous sur Global.asax

            GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);          

N'oubliez pas d'importer

using System.Data.Entity;

Ensuite, vous pouvez retourner vos propres modèles EF

Lucas Roselli
la source
3

Si vous utilisez une API Web avec Entity Framework, une solution peut échouer pour sérialiser la réponse dans l'API Web avec Json

Fondamentalement, vous devez créer un modèle correspondant à chaque modèle EF, cela supprime les dépendances entre les classes et permet une sérialisation facile.

Code: (extrait du lien référencé)

Créer un UserModel

public class UserModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Changer ma méthode GetAll ()

public IEnumerable<UserModel> GetAll()
{
    using (Database db = new Database ())
    {
        List<UserModel> listOfUsers = new List<UserModel>();
        UserModel userModel = new UserModel();
        foreach(var user in db.Users)
        {
           userModel.FirstName = user.FirstName;
           userModel.LastName = user.LastName;
           listOfUsers.Add(userModel);
        }
        IEnumerable<UserModel> users = listOfUsers;

        return users;
    }
}
Tung Nguyen
la source
2

L'entité par défaut 6 utilise XML pour apis, dans votre projet, recherchez le fichier "Global.asax" File et ajoutez cette ligne:

GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

Cette ligne supprime le formateur XML.

Roberth Solís
la source
Bonjour, l'API Web sérialise la réponse en XML et JSON, si vous ajoutez un en-tête Content-Type: application / json, la réponse est en JSON, vous devez définir cet en-tête, dans le navigateur, vous pouvez toujours le voir au format XML
Roberth Solís
1

mais si vous avez trouvé ce problème avec d'autres entités / classes, vous devez créer un nouveau DTO pour chaque classe, et si vous en avez beaucoup, vous pouvez trouver un problème, aussi je pense que créer un DTO uniquement pour résoudre ce problème n'est pas la meilleure façon ...

Avez-vous essayé cela?

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = 
Newtonsoft.Json.PreserveReferencesHandling.All;

Cordialement

Julito Avellaneda
la source
1

hmmm, ce qui suit peut vous aider.

J'obtenais la même exception, et dans mon cas, je passais d'abord l'entité poco réelle créée pour le code d'entité. Depuis, il contient une relation avec d'autres entités, je viens de créer l'entité viewmapper / dto par-dessus pour retourner.

Cela fonctionne bien maintenant.

Entité Poco:

public class Tag
{
public int Id{get;set;}
public string Title{get;set;}
public IList<Location> Locations{get;set;}
}

ViewMapper / Dto

public class TagResultsViewMapper
{
public int Id{get;set;}
public string Title{get;set;}
//just remove the following relationship 
//public IList<Location> Locations{get;set;}
}
aamir sajjad
la source
0

Votre question est assez similaire à la mienne. Vous ne devez pas renvoyer directement les données de la base de données. Pour cela, vous devez créer le modèle et associer les données que vous souhaitez afficher.

Dans mon exemple, il y a des données sur User que Json n'a pas pu sérialiser, j'ai créé un userModel et, dans mon API, je renvoie userModel à la place User de la base de données.

La logique de conversion ou d'association de données entre User et UserModel doit être dans l'API.

Échec de la sérialisation de la réponse dans l'API Web avec Json

CampDev
la source
0

C'était l'erreur spécifique que je recevais de mon appel à l'API Web odata:

The 'ObjectContent`1' type failed to serialize the response 
body for content type 'application/json; odata.metadata=minimal'.

J'ai finalement compris que ma classe dbContext avait un nom de table mal formaté attribué dans onModelCreating .. donc le SqlClient mourait à la recherche d'une table qui n'existait pas dans ma base de données !!

bkwdesign
la source