Fehler beim Serialisieren der Antwort in der Web-API mit Json


109

Ich arbeite mit ASP.NET MVC 5 Web Api. Ich möchte alle meine Benutzer konsultieren.

Ich habe geschrieben api/usersund erhalte Folgendes:

"Der Typ 'ObjectContent`1' konnte den Antworttext für den Inhaltstyp 'application / json; charset = utf-8' nicht serialisieren."

In WebApiConfig habe ich bereits folgende Zeilen hinzugefügt:

HttpConfiguration config = new HttpConfiguration();
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

Aber es funktioniert immer noch nicht.

Meine Funktion für Rückgabedaten lautet:

public IEnumerable<User> GetAll()
{
    using (Database db = new Database())
    {
        return db.Users.ToList();
    }
}

Wie sieht das Wertobjekt aus, das Sie an den Verbraucher weitergeben möchten?
McKejjm

Vielen Dank! Nur zu Ihrer Information - ich denke, das sollte lauten: using (Database db = new Database ()) {List <UserModel> listOfUsers = new List <UserModel> (); foreach (var user in db.Users) {UserModel userModel = new UserModel (); userModel.FirstName = user.FirstName; userModel.LastName = user.LastName; listOfUsers.Add (userModel); } IEnumerable <UserModel> users = listOfUsers; wiederkehrende Benutzer; } ..als die Ergebnisse alle die gleichen Werte zurückgaben.
Jared Whittington

Antworten:


76

Wenn es darum geht, Daten von Web Api (oder einem anderen Webdienst) an den Verbraucher zurückzugeben, empfehle ich dringend, Entitäten, die aus einer Datenbank stammen, nicht zurückzugeben. Es ist viel zuverlässiger und wartbarer, Modelle zu verwenden, in denen Sie steuern können, wie die Daten aussehen, und nicht die Datenbank. Auf diese Weise müssen Sie nicht so viel mit den Formatierern in der WebApiConfig herumspielen. Sie können einfach ein UserModel erstellen, das untergeordnete Modelle als Eigenschaften hat, und die Referenzschleifen in den Rückgabeobjekten entfernen. Das macht den Serializer viel glücklicher.

Außerdem müssen Formatierer oder unterstützte Medientypen normalerweise nicht entfernt werden, wenn Sie in der Anforderung nur den Header "Akzeptiert" angeben. Das Herumspielen mit diesem Zeug kann die Dinge manchmal verwirrender machen.

Beispiel:

public class UserModel {
    public string Name {get;set;}
    public string Age {get;set;}
    // Other properties here that do not reference another UserModel class.
}

Wenn Sie sich auf Models beziehen, möchten Sie sagen, was ich tue? Gibt IEnumerable von Benutzern zurück, die ein Modell sind.
CampDev

5
Sie geben eine Entität zurück. Eine Entität bezieht sich auf ein Objekt in der Datenbank, das von einer eindeutigen ID abgerufen werden kann. Sie geben alle Benutzerentitäten aus Ihrer Datenbank zurück. Ich schlage vor, dass Sie eine neue Klasse mit dem Namen "UserModel" erstellen und für jede Benutzerentität, die Sie aus der Datenbank erhalten, eine neue Instanz der Datenmodellklasse erstellen, die mit den erforderlichen Informationen gefüllt ist, die Sie verfügbar machen möchten. Die Rückgabe einer IEnumerable von UserModel-Objekten im Gegensatz zu User-Entitäten. Stellen Sie sicher, dass sich die Modelleigenschaften nicht auf Instanzen der UserModel-Klasse beziehen. Das ist es, was Sie in dieses Problem bringt.
Jensendp

3
ncampuzano ist korrekt, Sie möchten keine automatisch generierten Entitäten zurückgeben. Wenn Sie gespeicherte Prozeduren für den Datenbankzugriff verwenden, ist die Trennung klarer. Sie müssten ein C # -Wertobjekt generiert und Werte vom IDataReader dem Wertobjekt zugeordnet haben. Da Sie EF verwenden, werden Klassen für Sie generiert, aber dies sind spezielle EF-Klassen, die mehr wissen als Wertobjekte. Sie sollten nur "dumme" Wertobjekte an Ihren Client zurückgeben.
McKejjm

1
@Donny Wenn Sie DBContext oder ein Repository in Ihrem Controller verwenden, das Entitäten aus der Datenbank zurückgibt, können Sie die Objekte einfach Modellen (z. B. einem DTO) im Controller zuordnen ... aber ich bevorzuge das Der Controller ruft einen Dienst auf, der das Modell / DTO zurückgibt. Schauen Sie sich AutoMapper an - ein großartiges Tool für die Handhabung von Mappings.
Ben

1
@ NH. Sie können absolut die oben genannten Spielereien verwenden, aber alles hat seinen Platz. Die "Entitäten", die durch den Zugriff auf die Datenschicht bereitgestellt werden, sollten normalerweise in der Datenschicht verbleiben. Alles, was diese Daten in der Business-Schicht der Anwendung verwenden möchte, verwendet die "Entitäten" normalerweise auch in transformierter Form (Domänenobjekte). Und dann sind die Daten, die an den Benutzer zurückgegeben und von ihm eingegeben werden, normalerweise auch eine andere Form (Modelle). Einverstanden, dass es mühsam sein kann, diese Art der Transformation überall durchzuführen, aber hier sind Tools wie AutoMapper wirklich nützlich.
Jensendp

147

Wenn Sie mit EF arbeiten, fügen Sie neben dem folgenden Code auch Global.asax hinzu

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

Vergessen Sie nicht zu importieren

using System.Data.Entity;

Dann können Sie Ihre eigenen EF-Modelle zurückgeben

So einfach ist das!


Auch wenn es für EF hilfreich sein könnte, ist die Lösung nicht EF-spezifisch und funktioniert auch mit anderen Modellen. Die Verwendung scheint in Global.asax nicht erforderlich zu sein. War es für die Controller gedacht?
Matthieu

16
Einige Erklärungen darüber, was dieser Code tut und welche Auswirkungen er hat, wären zu begrüßen.
Jacob

1
Vielen Dank, dass ich vor einem ähnlichen Problem stand. Diese Antwort half mir, das Problem zu lösen.
RK_Aus

Für mich geht das. Keine Notwendigkeit, mit System.Data.Entity hinzuzufügen; zu global.asax. Danke dir.
Dr. MAF

Es klappt. Einfacher Code oben auf Global.asax hinzufügen, das ist alles, kein Import mit System.Data.Entity erforderlich;
Hemant Ramphul

52

Die richtige Antwort ist ein Weg, aber es ist ein Overkill, wenn Sie es durch eine Konfigurationseinstellung beheben können.

Besser im dbcontext-Konstruktor verwenden

public DbContext() // dbcontext constructor
            : base("name=ConnectionStringNameFromWebConfig")
{
     this.Configuration.LazyLoadingEnabled = false;
     this.Configuration.ProxyCreationEnabled = false;
}

Asp.Net-Web-API-Fehler: Der Typ 'ObjectContent`1' konnte den Antworttext für den Inhaltstyp 'application / xml nicht serialisieren. Zeichensatz = utf-8 '


Ihr Code wird entfernt, wenn wir das Modell aus der Datenbank aktualisieren.
Bimal Das

1
Sie können es einfach trennen, indem Sie die .tt-Dateien entfernen und den Kontext trennen. Jedes Mal, wenn Sie ein Modell generieren, fügen Sie einfach eine neue Klasse hinzu. @Brimal: Sie können diesem youtube.com/watch?v=yex0Z6qwe7A
Md. Alim Ul Karim

1
Um ein Überschreiben zu vermeiden, können Sie das verzögerte Laden in den edmx-Eigenschaften deaktivieren. Es hat bei mir funktioniert.
Francisco G

@FranciscoG es funktioniert, aber es geht verloren, wenn wir edmx entfernen und neu generieren.
Md. Alim Ul Karim

1
@BimalDas versuchen Sie dies youtube.com/… . Es wird nicht entfernt
Md. Alim Ul Karim

37

Fügen Sie diesen Code global.asaxunten hinzu Application_Start:

Update von .Ignorebis .Serialize. Es muss funktionieren.

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

1
Funktioniert großartig, danke! Könnten Sie besser erklären, warum wir den XML-Formatierer entfernen müssen, damit er funktioniert?
Jav_1

Es ist nicht erforderlich, den JSON-Serialisierer hinzuzufügen (zumindest in meinem Fall), aber das Entfernen der XML-Formatierung war erforderlich. Ich denke, der XML-Serialisierer kann keine anonymen Typen serialisieren, und durch Entfernen wird das Ergebnis als json serialisiert. Wenn meine Vermutung richtig ist, kann man Daten aus dem Controller abrufen, indem man nach dem MIME-Typ "application / json" fragt.
LosManos

11
public class UserController : ApiController
{

   Database db = new Database();

   // construction
   public UserController()
   {
      // Add the following code
      // problem will be solved
      db.Configuration.ProxyCreationEnabled = false;
   }

   public IEnumerable<User> GetAll()
    {
            return db.Users.ToList();
    }
}

Wow, es hat bei mir funktioniert. Aber wieso? Was macht die ProxyCreationEnabled-Eigenschaft?
Jacktric

hat bei mir aber was mit diesem code gearbeitet? Ich habe auch bemerkt, dass alle Unterklassen mit null abgerufen wurden !!
Waleed A. Elgalil

10

Ich mag diesen Code nicht:

foreach(var user in db.Users)

Als Alternative könnte man so etwas tun, was für mich funktioniert hat:

var listOfUsers = db.Users.Select(r => new UserModel
                         {
                             userModel.FirstName = r.FirstName;
                             userModel.LastName = r.LastName;

                         });

return listOfUsers.ToList();

Am Ende habe ich jedoch die Lösung von Lucas Roselli verwendet.

Update: Vereinfacht durch Rückgabe eines anonymen Objekts:

var listOfUsers = db.Users.Select(r => new 
                         {
                             FirstName = r.FirstName;
                             LastName = r.LastName;
                         });

return listOfUsers.ToList();

10

Ich habe es mit diesem Code in die Datei WebApiConfig.cs aufgelöst

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; 
config.Formatters.Remove(config.Formatters.XmlFormatter);

Vielen Dank. Ich weiß jedoch nicht, was dies für die Sicherheit bedeutet.
Arun Prasad ES

6

Es gibt auch dieses Szenario, das denselben Fehler erzeugt:

Im Falle, dass die Rückgabe eine List<dynamic>Web-API-Methode ist

Beispiel:

public HttpResponseMessage Get()
{
    var item = new List<dynamic> { new TestClass { Name = "Ale", Age = 30 } };

    return Request.CreateResponse(HttpStatusCode.OK, item);
}

public class TestClass
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Verwenden Sie für dieses Szenario das [KnownTypeAttribute] in der Rückgabeklasse (alle) wie folgt:

[KnownTypeAttribute(typeof(TestClass))]
public class TestClass
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Das funktioniert bei mir!


Was ist, wenn Sie Linq Pivot mit dynamischen Spalten verwenden?
Codegrid

[KnownTypeAttribute (typeof (TestClass))] hat mein Problem gelöst
Guillaume Raymond

6

Addiert man diese in Ihrer Application_Start()Methode der Global.asaxDatei lösen soll das Problem

protected void Application_Start()
{
    GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
        .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    GlobalConfiguration.Configuration.Formatters
        .Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter); 
// ...
}

METHODE 2: [Nicht empfohlen]
Wenn Sie mit EntityFramework arbeiten, können Sie den Proxy in Ihrem DbContext-Klassenkonstruktor deaktivieren. HINWEIS: Dieser Code wird entfernt, wenn Sie das Modell aktualisieren

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.ProxyCreationEnabled = false;
  }
}

1
Danke Suman. Ich hatte das gleiche Problem. Ich habe meine Web-API getestet und bin bei diesem Problem geblieben. Ihre Lösung behebt das Problem. Vielen Dank.
TarakPrajapati

4

Mein persönlicher Favorit: Fügen Sie einfach den folgenden Code hinzu App_Start/WebApiConfig.cs. Dies gibt standardmäßig json anstelle von XML zurück und verhindert auch den Fehler, den Sie hatten. Keine Notwendigkeit zu bearbeiten Global.asax, um zu entfernen XmlFormatterusw.

Der Typ 'ObjectContent`1' konnte den Antworttext für den Inhaltstyp 'application / xml nicht serialisieren. Zeichensatz = utf-8

config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

2

Verwenden Sie AutoMapper ...

public IEnumerable<User> GetAll()
    {
        using (Database db = new Database())
        {
            var users = AutoMapper.Mapper.DynamicMap<List<User>>(db.Users);
            return users;
        }
    }

2

Verwenden Sie den folgenden Namespace:

using System.Web.OData;

Anstatt :

using System.Web.Http.OData;

Es hat bei mir funktioniert


2

Fügen Sie die folgende Zeile hinzu

this.Configuration.ProxyCreationEnabled = false;

Zwei Möglichkeiten, ProxyCreationEnabledals zu verwenden false.

  1. Fügen Sie es in DBContextConstructor hinzu

    public ProductEntities() : base("name=ProductEntities")
    {
        this.Configuration.ProxyCreationEnabled = false;
    }

ODER

  1. Fügen Sie die Zeile innerhalb der GetMethode hinzu

    public IEnumerable<Brand_Details> Get()
    {
        using (ProductEntities obj = new ProductEntities())
        {
            this.Configuration.ProxyCreationEnabled = false;
            return obj.Brand_Details.ToList();
        }
    }

2

Lösung, die bei mir funktioniert hat:

  1. Verwenden Sie [DataContract]für Klasse und [DataMember]Attribute für jede zu serialisierende Eigenschaft. Dies reicht aus, um das Json-Ergebnis zu erhalten (z. B. vom Geiger).

  2. Um die XML-Serialisierung zu erhalten, schreiben Sie Global.asaxdiesen Code:

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; xml.UseXmlSerializer = true;

  3. Lesen Sie diesen Artikel, er hat mir geholfen, die Serialisierung zu verstehen: https://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization

1

Um die Antwort von jensendp zu ergänzen:

Ich würde die Entität an ein vom Benutzer erstelltes Modell übergeben und die Werte dieser Entität verwenden, um die Werte in Ihrem neu erstellten Modell festzulegen. Beispielsweise:

public class UserInformation {
   public string Name { get; set; }
   public int Age { get; set; }

   public UserInformation(UserEntity user) {
      this.Name = user.name;
      this.Age = user.age;
   }
}

Ändern Sie dann Ihren Rückgabetyp in: IEnumerable<UserInformation>


1
Es gibt elegantere Möglichkeiten, die Übersetzung für Sie zu erledigen, damit Sie nicht jede Eigenschaft pflegen müssen. AutoMapper und ValueInjecter sind zwei bemerkenswerte
Sonic Soul

1

Das ist mein Fehler

Ich füge im Grunde eine Zeile hinzu, die sie sind

  • entity.Configuration.ProxyCreationEnabled = false;

zu UsersController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using UserDataAccess;

namespace SBPMS.Controllers
{
    public class UsersController : ApiController
    {


        public IEnumerable<User> Get() {
            using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
                entities.Configuration.ProxyCreationEnabled = false;
                return entities.Users.ToList();
            }
        }
        public User Get(int id) {
            using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
                entities.Configuration.ProxyCreationEnabled = false;
                return entities.Users.FirstOrDefault(e => e.user_ID == id);
            }
        }
    }
}

Hier ist meine Ausgabe:


1

Verwenden Sie [Serializable] für die Klasse:

Beispiel:

[Serializable]
public class UserModel {
    public string Name {get;set;}
    public string Age {get;set;}
}

Es hat bei mir funktioniert!


1
So formuliert könnte man fast denken, dass jeder, der diesen Beitrag zuvor beantwortet hat, dumm war;)… Nun, ich bezweifle ernsthaft, dass dies der Fall war, was wahrscheinlich bedeutet, dass diese Lösung einfach nicht anwendbar war, als die Frage im Jahr 2015 gestellt wurde… Ich weiß selbst nicht viel über diese Syntax, aber ich habe das Gefühl, dass sie entweder relativ neu ist oder einige Nachteile aufweist, die sie in bestimmten Anwendungsfällen unbrauchbar machen. Ich habe das Gefühl, dass Ihre Lösung für zukünftige Leser, die diese Frage beantworten, nützlich sein könnte, aber es wäre sicherlich hilfreich, wenn Sie ihre Grenzen klären würden.
Jwatkins

1

Sie müssen den Serializer-Formatierer in WebApiConfig.cs definieren, der im Ordner App_Start verfügbar ist

Hinzufügen von config.Formatters.Remove (config.Formatters.XmlFormatter); // die Ihnen Daten im JSON-Format liefert

Hinzufügen von config.Formatters.Remove (config.Formatters.JsonFormatter); // die Ihnen Daten im XML-Format liefert


Sie verdienen eine Medaille.
TheKrogrammer

0

Ein anderer Fall, in dem ich diesen Fehler erhielt, war, als meine Datenbankabfrage einen Nullwert zurückgab, mein Benutzer- / Ansichtsmodelltyp jedoch als nicht nullwertfähig festgelegt wurde. Zum Beispiel den Wechsel von meinem UserModel Feld intzu int?aufgelöst.


0

Dies passiert auch, wenn der Antworttyp nicht öffentlich ist! Ich habe eine interne Klasse zurückgegeben, als ich mit Visual Studio den Typ generiert habe.

internal class --> public class

0

Obwohl alle oben genannten Antworten korrekt sind, sollten Sie InnerException> ExceptionMessage überprüfen .

Wenn so etwas wie "Die ObjectContext-Instanz wurde entsorgt und kann nicht mehr für Vorgänge verwendet werden, für die eine Verbindung erforderlich ist. " Dies kann aufgrund des Standardverhaltens der EF ein Problem sein.

Durch Zuweisen von LazyLoadingEnabled = false in Ihrem DbContext-Konstruktor wird der Trick ausgeführt.

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.LazyLoadingEnabled = false;
  }
}

Weitere Informationen zum EagerLoading- und LazyLoading-Verhalten von EF finden Sie in diesem MSDN-Artikel .


0

In meinem Fall hatte ich eine ähnliche Fehlermeldung:

Der Typ 'ObjectContent`1' konnte den Antworttext für den Inhaltstyp 'application / xml nicht serialisieren. Zeichensatz = utf-8 '.

Aber wenn ich tiefer darin grabe, war das Problem:

Geben Sie 'name.SomeSubRootType' mit dem Datenvertragsnamen 'SomeSubRootType: //schemas.datacontract.org/2004/07/WhatEverService' nicht ein. Verwenden Sie einen DataContractResolver, wenn Sie DataContractSerializer verwenden, oder fügen Sie der Liste der bekannten Typen nicht statisch bekannte Typen hinzu, indem Sie beispielsweise das Attribut KnownTypeAttribute verwenden oder sie der Liste der bekannten Typen hinzufügen, die an den Serializer übergeben werden.

Die Art und Weise, wie ich durch Hinzufügen gelöst habe KnownType.

[KnownType(typeof(SomeSubRootType))]
public partial class SomeRootStructureType

Dies wurde inspiriert von dieser Antwort gelöst .

Referenz: https://msdn.microsoft.com/en-us/library/ms730167(v=vs.100).aspx


0

Visual Studio 2017 oder 2019 ist diesbezüglich völlig unüberlegt, da Visual Studio selbst die Ausgabe im JSON- Format erfordert , während das Standardformat von Visual Studio " XmlFormat" (config.Formatters.XmlFormatter) ist .

Visual Studio sollte dies automatisch tun, anstatt den Entwicklern so viel Ärger zu bereiten.

Um dieses Problem zu beheben, rufen Sie die Datei WebApiConfig.cs auf und fügen Sie hinzu

var json = config.Formatters.JsonFormatter; json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; config.Formatters.Remove (config.Formatters.XmlFormatter);

nach " config.MapHttpAttributeRoutes (); " in der Register- Methode (HttpConfiguration config) . Dies würde es Ihrem Projekt ermöglichen, eine JSON-Ausgabe zu erzeugen.


0

In meinem Fall habe ich die Neuerstellung der Datenbank gelöst. Ich habe einige Änderungen an einem Modell vorgenommen und beim Starten der Update-Datenbank in der Package Manager-Konsole den folgenden Fehler erhalten:

"Die Anweisung ALTER TABLE stand in Konflikt mit der FOREIGN KEY-Einschränkung" FK_dbo.Activities_dbo.Projects_ProjectId ". Der Konflikt trat in der Datenbank" TrackEmAllContext-20190530144302 ", Tabelle" dbo.Projects ", Spalte 'Id' auf."


0

Falls: Wenn das Hinzufügen von Code zu WebApiConfig.cs oder Global.asax.cs für Sie nicht funktioniert:

.ToList();

Fügen Sie die Funktion .ToList () hinzu.

Ich habe jede Lösung ausprobiert, aber Folgendes hat bei mir funktioniert:

var allShops = context.shops.Where(s => s.city_id == id)**.ToList()**;
return allShops;

Ich hoffe, es hilft.


0

In meinem Fall wurde es behoben, als ich das virtuelle Schlüsselwort vor meinen Navigationseigenschaften entfernte. Ich meine die Referenztabellen. Also habe ich mich verändert

public virtual MembershipType MembershipType { get; set; }

zu:

public MembershipType MembershipType { get; set; }

0

Fügen Sie einfach die folgenden Zeilen in global.asax ein:

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

Importieren

using System.Data.Entity;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.