rest - Error al serializar el cuerpo de respuesta para el tipo de contenido
fluent-nhibernate asp.net-mvc-4 (3)
Estoy construyendo una aplicación MVC4 que usa controladores y ApiControllers. Modifiqué la ruta predeterminada de la API web para incluir nombres de acción. Cuando trato de obtener una lista de Benchmarks, recibo este mensaje de error:
El tipo ''ObjectContent`1'' no pudo serializar el cuerpo de la respuesta para el tipo de contenido ''application / json; charset = utf-8 ''
La InnerException es esta (en este caso, devuelvo JSON, sucede lo mismo con XML):
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Error getting value from ''IdentityEqualityComparer'' on ''NHibernate.Proxy.DefaultLazyInitializer''.",
"ExceptionType": "Newtonsoft.Json.JsonSerializationException",
"StackTrace": " at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeISerializable(JsonWriter writer, ISerializable value, JsonISerializableContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IWrappedCollection values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value) at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value) at System.Net.Http.Formatting.JsonMediaTypeFormatter.<>c__DisplayClassd.<WriteToStreamAsync>b__c() at System.Threading.Tasks.TaskHelpers.RunSynchronously(Action action, CancellationToken token)",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Common Language Runtime detected an invalid program.",
"ExceptionType": "System.InvalidProgramException",
"StackTrace": " at GetIdentityEqualityComparer(Object ) at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)"
}
Este es el código que corro:
// GET api/benchmark/getincomplete
[HttpGet]
public IList<Benchmark> GetIncomplete()
{
var s = HibernateModule.CurrentSession;
var benchList = s.QueryOver<Benchmark>()
.Where(b => !b.Completed)
.Where(b => !b.Deleted)
.OrderBy(b => b.Id).Asc
.List<Benchmark>();
return benchList;
}
Y este es el modelo de Benchmark:
public class Benchmark
{
public virtual int Id { get; set; }
[Required]
[DataType(DataType.Date)]
public virtual DateTime Date { get; set; }
[Required, ScriptIgnore]
public virtual IList<TestResult> Results { get; set; }
[Required]
public virtual IList<TestCase> TestCases { get; set; }
[AllowHtml]
public virtual string Description { get; set; }
public virtual Device Device { get; set; }
public virtual bool Published { get; set; }
[Display(Name = "Deleted"), ScriptIgnore]
public virtual bool Deleted { get; set; }
public virtual bool Completed { get; set; }
public Benchmark()
{
Results = new List<TestResult>();
TestCases = new List<TestCase>();
Published = false;
Deleted = false;
Completed = false;
}
}
No estoy seguro de dónde está el problema. ¿Podría ser el NHibernate Proxy (yo uso Fluent NHibernate)? Lo extraño es que si no uso un ApiController y devuelvo manualmente JSON, ¡esto funciona a la perfección!
Actualizar:
Según la respuesta a continuación, este es el código que tuve que agregar en Application_Start ():
HttpConfiguration config = GlobalConfiguration.Configuration;
((DefaultContractResolver)config.Formatters.JsonFormatter.SerializerSettings.ContractResolver).IgnoreSerializableAttribute = true;
hmmm, lo siguiente puede ayudar.
Recibía la misma excepción, y en mi caso, primero estaba pasando la entidad poco real creada para el código de entidad. Como contiene propiedades de navegación, como IList Locations, acabo de crear la entidad viewmapper / dto encima para devolver.
Funciona ahora.
Entidad 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;}
}
Esto soluciona el error JSON, buena suerte con XML. Agregue esto a la clase WebApiConfig debajo del Método de registro.
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling=Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
ACTUALIZAR:
Encontré dos soluciones para esto. El primero y más fácil de implementar es cambiar cualquier IEnumerables, ICollections a un tipo de lista. La WebAPI puede serializar estos objetos, sin embargo, no puede serializar tipos de interfaz.
public class Store
{
[StringLength(5)]
public string Zip5 { get; set; }
public virtual List<StoreReport> StoreReports { get; set; } //use a list here
}
La otra opción es no usar el serializador JSON nativo. El primer método que publiqué todavía funciona.
IdentityEqualityCompairer
en el marco NHibernate parece tener la anotación [SerializableAttribute]
.
Tomado de un comentario sobre una respuesta aquí ¿Por qué la API Web no deserializará esto sino que lo hará JSON.Net? parece que JSON.NET está configurado de forma un poco diferente entre el uso de WebApi y su uso independiente.
El serializador Json.NET establece de manera predeterminada IgnoreSerializableAttribute en verdadero. En WebAPI, lo configuramos como falso.
Por lo tanto, dado que no puede desactivar [SerializableAttribute]
(ya que no está dentro de su código) puede intentar cambiar la configuración predeterminada de WebApi JSON.NET para ignorarla usando esta respuesta aquí :
((DefaultContractResolver)config.Formatters.JsonFormatter.SerializerSettings.ContractResolver).IgnoreSerializableAttribute = true;
Tal vez también considere usar un DTO para enviar los resultados en la respuesta, esto le dará control total sobre el objeto que se envía por cable.