No se pudo serializar la respuesta en la API web con Json

109

Estoy trabajando con ASP.NET MVC 5 Web Api. Quiero consultar a todos mis usuarios.

Escribí api/usersy recibo esto:

"El tipo 'ObjectContent`1' no pudo serializar el cuerpo de la respuesta para el tipo de contenido 'application / json; charset = utf-8'"

En WebApiConfig, ya agregué estas líneas:

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

Pero todavía no funciona.

Mi función para devolver datos es esta:

public IEnumerable<User> GetAll()
{
    using (Database db = new Database())
    {
        return db.Users.ToList();
    }
}
CampDev
fuente
¿Qué aspecto tiene el objeto de valor que está tratando de transmitir al consumidor?
mckeejm
¡Muchas gracias! Solo para su información, creo que debería leer: using (Database db = new Database ()) {List <UserModel> listOfUsers = new List <UserModel> (); foreach (usuario var en db.Users) {UserModel userModel = new UserModel (); userModel.FirstName = user.FirstName; userModel.LastName = user.LastName; listOfUsers.Add (userModel); } IEnumerable <UserModel> users = listOfUsers; usuarios que regresan; } .. ya que todos los resultados devolvieron los mismos valores.
Jared Whittington
1
Posible duplicado de error al serializar la respuesta en Web API
Edvaldo Silva

Respuestas:

76

Cuando se trata de devolver datos al consumidor desde Web Api (o cualquier otro servicio web para el caso), recomiendo no devolver entidades que provengan de una base de datos. Es mucho más confiable y fácil de mantener usar modelos en los que tiene control de cómo se ven los datos y no la base de datos. De esa manera, no tendrá que perder tanto tiempo con los formateadores en WebApiConfig. Puede crear un UserModel que tenga modelos secundarios como propiedades y deshacerse de los bucles de referencia en los objetos de retorno. Eso hace que el serializador sea mucho más feliz.

Además, no es necesario eliminar los formateadores o los tipos de medios admitidos normalmente si solo especifica el encabezado "Acepta" en la solicitud. Jugar con esas cosas a veces puede hacer que las cosas sean más confusas.

Ejemplo:

public class UserModel {
    public string Name {get;set;}
    public string Age {get;set;}
    // Other properties here that do not reference another UserModel class.
}
jensendp
fuente
Cuando se refiere a modelos, ¿quiere decir qué estoy haciendo? Devuelve IEnumerable de usuarios que es un modelo.
CampDev
5
Estás devolviendo una entidad. Una entidad se refiere a un objeto en la base de datos que se puede recuperar mediante una identificación única. Está devolviendo todas las entidades de usuario de su base de datos. Le sugiero que cree una nueva clase llamada "UserModel" y para cada una de las entidades de usuario que obtenga de la base de datos, cree una nueva instancia de la clase de modelo de datos poblada con la información necesaria que desea exponer. Devuelve un IEnumerable de objetos UserModel en lugar de entidades de usuario. Asegúrese de que las Propiedades del modelo no se refieran a instancias de la clase UserModel. Eso es lo que te está metiendo en este problema.
jensendp
3
ncampuzano es correcto, no desea devolver entidades generadas automáticamente. Si estuviera utilizando procedimientos almacenados para acceder a la base de datos, la separación sería más clara. Debería haber generado un objeto de valor C # y haber asignado valores desde IDataReader al objeto de valor. Como está utilizando EF, se están generando clases para usted, pero esas son clases EF especiales que saben más de lo que saben los objetos de valor. Solo debe devolver objetos de valor "tontos" a su cliente.
mckeejm
1
@Donny Si está utilizando DBContext o un Repositorio en su controlador que está devolviendo entidades desde la base de datos, entonces puede simplemente asignar los objetos a los modelos (un DTO por ejemplo) en el controlador ... pero prefiero tener el El controlador llama a un servicio que devuelve el modelo / DTO. Consulte AutoMapper, una gran herramienta para manejar mapas.
ben
1
@NUEVA HAMPSHIRE. Absolutamente puedes usar las travesuras mencionadas anteriormente, pero todo tiene su lugar. Las "entidades" proporcionadas por el acceso a la capa de datos normalmente deben permanecer en la capa de datos. Todo lo que desee utilizar estos datos dentro de la capa empresarial de la aplicación, normalmente utilizará las "entidades" en una forma transformada también (Objetos de dominio). Y luego, los datos que se devuelven y la entrada del usuario normalmente también serán de otro formulario (Modelos). De acuerdo, puede ser tedioso hacer este tipo de transformación en todas partes, pero ahí es donde herramientas como AutoMapper son realmente útiles.
jensendp
147

Si está trabajando con EF, además de agregar el siguiente código en Global.asax

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

No olvides importar

using System.Data.Entity;

Entonces puede devolver sus propios modelos EF

¡Simple como eso!

Lucas Roselli
fuente
Incluso si pudiera ayudar para EF, la solución no es específica de EF y funciona también con otro tipo de modelos. El uso no parece ser necesario en Global.asax. ¿Estaba destinado a los controladores?
Matthieu
16
Se agradecerían algunas explicaciones sobre lo que hace este código y sus implicaciones.
Jacob
1
Gracias, estaba enfrentando un problema similar, esta respuesta me ayudó a resolver el problema.
RK_Aus
Esto funciona para mi. No es necesario agregar usando System.Data.Entity; a global.asax. Gracias.
Dr. MAF
Funciona. Simplemente agregue el código arriba en Global.asax, eso es todo, no es necesario importar usando System.Data.Entity;
Hemant Ramphul
52

Una respuesta correcta es una forma de hacerlo, sin embargo, es una exageración cuando puede solucionarlo mediante una configuración de configuración.

Es mejor usarlo en el constructor dbcontext

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

Error de API web de Asp.Net: el tipo 'ObjectContent`1' no pudo serializar el cuerpo de la respuesta para el tipo de contenido 'application / xml; charset = utf-8 '

Md. Alim Ul Karim
fuente
su código será eliminado si actualizamos el modelo de la base de datos.
Bimal Das
1
Puede separarlo fácilmente eliminando los archivos .tt y tener un contexto disjunto. Cada vez que genere un modelo, simplemente agregue una nueva clase en el lugar. @Brimal: Puedes seguir este youtube.com/watch?v=yex0Z6qwe7A
Md. Alim Ul Karim
1
Para evitar sobrescribir, puede desactivar la carga diferida, desde las propiedades de edmx. Funcionó para mí.
Francisco G
@FranciscoG funciona pero se pierde si eliminamos edmx y lo regeneramos.
Md. Alim Ul Karim
1
@BimalDas prueba este youtube.com/… . No se eliminará
Md. Alim Ul Karim
37

Agregue este código a global.asaxcontinuación en Application_Start:

Actualizar de .Ignorea .Serialize. Debe funcionar.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
            GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Bimal Das
fuente
1
Funciona muy bien, ¡gracias !, ¿podría explicarnos mejor por qué tenemos que eliminar el formateador Xml para que funcione?
Jav_1
No es necesario agregar el serializador json (al menos en mi caso), pero era necesario eliminar el formato Xml. Yo supongo que el serializador XML no puede serializar los tipos anónimos y quitándolo el resultado es serializado como JSON. Si mi conjetura es correcta, uno podría obtener datos del controlador solicitando el tipo MIME "application / json".
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();
    }
}
Aykut
fuente
Vaya, funcionó para mí. ¿Pero por qué? ¿Qué hace la propiedad ProxyCreationEnabled?
jacktric
trabajó conmigo, pero ¿qué es este código? También noté que todas las subclases recuperadas con null !!
Waleed A. Elgalil
10

No me gusta este código:

foreach(var user in db.Users)

Como alternativa, uno podría hacer algo como esto, que funcionó para mí:

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

                         });

return listOfUsers.ToList();

Sin embargo, terminé usando la solución de Lucas Roselli.

Actualización: simplificado al devolver un objeto anónimo:

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

return listOfUsers.ToList();
Kasper Halvas Jensen
fuente
10

Lo resolví usando este código en el archivo WebApiConfig.cs

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; 
config.Formatters.Remove(config.Formatters.XmlFormatter);
aemre
fuente
muchas gracias. Aunque no sé qué le hace esto a la seguridad.
Arun Prasad ES
6

También existe este escenario que genera el mismo error:

En caso de que la devolución sea un List<dynamic> método de API web

Ejemplo:

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; }
}

Entonces, para este escenario use el [KnownTypeAttribute] en la clase de retorno (todos ellos) así:

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

¡Esto funciona para mí!

Alex
fuente
¿Qué sucede si está utilizando linq pivot con columnas dinámicas?
Codegrid
[KnownTypeAttribute (typeof (TestClass))] resolvió mi problema
Guillaume Raymond
6

Agregar esto en su Application_Start()método de Global.asaxarchivo debería resolver el problema

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

MÉTODO 2: [No recomendado]
Si está trabajando con EntityFramework, puede deshabilitar el proxy en su constructor de clases DbContext. NOTA: este código se eliminará si actualiza el modelo

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.ProxyCreationEnabled = false;
  }
}
Er Suman G
fuente
1
Gracias Suman. Yo estaba teniendo el mismo problema. Estaba probando mi API web y me quedé con este problema. su solución resuelve el problema. muchas gracias.
TarakPrajapati
4

Mi favorito personal: simplemente agregue el código a continuación a App_Start/WebApiConfig.cs. Esto devolverá json en lugar de XML de forma predeterminada y también evitará el error que tuvo. No es necesario editar Global.asaxpara eliminar, XmlFormatteretc.

El tipo 'ObjectContent`1' no pudo serializar el cuerpo de la respuesta para el tipo de contenido 'application / xml; juego de caracteres = utf-8

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

Usar AutoMapper ...

public IEnumerable<User> GetAll()
    {
        using (Database db = new Database())
        {
            var users = AutoMapper.Mapper.DynamicMap<List<User>>(db.Users);
            return users;
        }
    }
Del mes próximo
fuente
2

Utilice el siguiente espacio de nombres:

using System.Web.OData;

En vez de :

using System.Web.Http.OData;

Funcionó para mi

Harish K
fuente
2

Agrega la siguiente línea

this.Configuration.ProxyCreationEnabled = false;

Dos formas de usar ProxyCreationEnabledcomo false.

  1. Agréguelo dentro de DBContextConstructor

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

O

  1. Agrega la línea dentro del Getmétodo

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

Solución que funcionó para mí:

  1. Úselo [DataContract]para la clase y los [DataMember]atributos de cada propiedad a serializar. Esto es suficiente para obtener el resultado de Json (por ejemplo, de fiddler).

  2. Para obtener la serialización xml, escriba Global.asaxeste código:

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

  3. Leer este artículo, me ayudó a comprender la serialización: https://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
Vladimir Lapenkov
fuente
1

Para agregar a la respuesta de jensendp:

Pasaría la entidad a un modelo creado por el usuario y usaría los valores de esa entidad para establecer los valores en su modelo recién creado. Por ejemplo:

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;
   }
}

Luego cambie su tipo de retorno a: IEnumerable<UserInformation>

Greg A
fuente
1
hay formas más elegantes de manejar la traducción para que no tenga que mantener todas las propiedades. AutoMapper y ValueInjecter son 2 notables
Sonic Soul
1

Este es mi error

Básicamente agrego una línea que son

  • entidades.Configuración.ProxyCreationEnabled = falso;

a 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);
            }
        }
    }
}

Aquí está mi salida:

GUL EDA AYDEMİR
fuente
1

Utilice [Serializable] para la clase:

Ejemplo:

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

¡Funcionó para mí!

Msxmania
fuente
1
Expresado así, uno casi podría pensar que todos los que respondieron esta publicación antes eran tontos;) ... Bueno, dudo seriamente que lo fueran, por lo que probablemente significa que esta solución simplemente no era aplicable cuando se hizo la pregunta, en 2015 ... Yo mismo no sé mucho sobre esa sintaxis, pero tengo la sensación de que es relativamente nueva o que puede tener algunos inconvenientes que la hacen inutilizable en ciertos casos de uso. Tengo la sensación de que su solución podría ser útil para los futuros lectores que lleguen a esta pregunta, pero ciertamente ayudaría si aclarara sus limitaciones.
jwatkins
1

Tendrá que definir Serializer Formatter dentro de WebApiConfig.cs disponible en App_Start Folder como

Añadiendo config.Formatters.Remove (config.Formatters.XmlFormatter); // que le proporcionará datos en formato JSON

Añadiendo config.Formatters.Remove (config.Formatters.JsonFormatter); // que le proporcionará datos en formato XML

Yogesh Dangre
fuente
Te mereces una medalla.
TheKrogrammer
1

Simplemente coloque las siguientes líneas en global.asax:

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

Importar

using System.Data.Entity;
samu abbasi
fuente
0

Otro caso en el que recibí este error fue cuando mi consulta de base de datos devolvió un valor nulo, pero mi tipo de modelo de usuario / vista se estableció como no anulable. Por ejemplo, cambiar mi campo UserModel de inta int?resuelto.

Marshall
fuente
0

¡Esto también sucede cuando el tipo de respuesta no es público! Devolví una clase interna ya que usé Visual Studio para generarme el tipo.

internal class --> public class
Vanice
fuente
0

Si bien todas estas respuestas anteriores son correctas, es posible que desee verificar InnerException> ExceptionMessage .

Si dice algo como esto "La instancia de ObjectContext se ha eliminado y ya no se puede usar para operaciones que requieren una conexión. ". Esto podría ser un problema debido al comportamiento predeterminado del EF.

Al asignar LazyLoadingEnabled = false en su constructor DbContext hará el truco.

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

Para obtener una lectura más detallada sobre el comportamiento EagerLoading y LazyLoading de EF, consulte este artículo de MSDN .

Bhramar
fuente
0

En mi caso, he tenido un mensaje de error similar:

El tipo 'ObjectContent`1' no pudo serializar el cuerpo de la respuesta para el tipo de contenido 'application / xml; charset = utf-8 '.

Pero cuando profundizo en él, el problema fue:

Escriba 'name.SomeSubRootType' con el nombre del contrato de datos 'SomeSubRootType: //schemas.datacontract.org/2004/07/WhatEverService' no se espera. Considere usar un DataContractResolver si está usando DataContractSerializer o agregue cualquier tipo no conocido estáticamente a la lista de tipos conocidos, por ejemplo, usando el atributo KnownTypeAttribute o agregándolos a la lista de tipos conocidos pasados ​​al serializador.

La forma en que resolví agregando KnownType.

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

Esto se resolvió inspirado en esta respuesta .

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

maytham-ɯɐɥʇʎɐɯ
fuente
0

Visual Studio 2017 o 2019 es totalmente imprudente en esto, porque Visual Studio requiere que la salida esté en formato json , mientras que el formato predeterminado de Visual Studio es " XmlFormat" (config.Formatters.XmlFormatter) .

Visual Studio debería hacer esto automáticamente en lugar de dar tantos problemas a los desarrolladores.

Para corregir este problema, vaya al archivo WebApiConfig.cs y agregue

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

después de " config.MapHttpAttributeRoutes (); " en el método Register (HttpConfiguration config) . Esto permitiría que su proyecto produzca una salida json.

William Hou
fuente
0

En mi caso resolví recrear la base de datos. Hice algunos cambios en un modelo y al iniciar Update-Database en Package Manager Console obtuve el siguiente error:

"La declaración ALTER TABLE entró en conflicto con la restricción FOREIGN KEY" FK_dbo.Activities_dbo.Projects_ProjectId ". El conflicto ocurrió en la base de datos" TrackEmAllContext-20190530144302 ", tabla" dbo.Projects ", columna 'Id'."

Manuel Sansone
fuente
0

En caso de que no le funcione agregar código a WebApiConfig.cs o Global.asax.cs :

.ToList();

Agregue la función .ToList ().

Probé todas las soluciones, pero lo siguiente funcionó para mí:

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

Espero que ayude.

Catalizador
fuente
0

en mi caso, se solucionó cuando eliminé la palabra clave virtual antes de mis propiedades de navegación, me refiero a las tablas de referencia. así que cambié

public virtual MembershipType MembershipType { get; set; }

a:

public MembershipType MembershipType { get; set; }
Hadi Rezaee
fuente