web-dev-qa-db-fra.com

Échec de la sérialisation du corps de la réponse pour le type de contenu

Je construis une application MVC4 qui utilise à la fois des contrôleurs et des ApiControllers. J'ai modifié la route API Web par défaut pour inclure les noms d'action. Lorsque j'essaye d'obtenir une liste de Benchmarks, je reçois ce message d'erreur:

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

La InnerException est la suivante (je retourne JSON dans ce cas, il en va de même avec 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)"
}

C'est le code que j'exécute:

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

Et voici le modèle 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;
        }
    }

Je ne sais pas trop où se situe le problème. Serait-ce le NHibernate Proxy (j'utilise Fluent NHibernate)? La chose étrange est que si je n'utilise pas d'ApiController et que je retourne manuellement JSON, cela fonctionne parfaitement!

Mettre à jour:

Selon la réponse ci-dessous, c'est le code que j'ai dû ajouter dans Application_Start ():

HttpConfiguration config = GlobalConfiguration.Configuration;
((DefaultContractResolver)config.Formatters.JsonFormatter.SerializerSettings.ContractResolver).IgnoreSerializableAttribute = true;
16
Astaar

Le IdentityEqualityCompairer dans le framework NHibernate semble avoir le [SerializableAttribute] annotation.

Tiré d'un commentaire sur une réponse ici Pourquoi l'API Web ne désérialise-t-elle pas cela mais JSON.Net le fera? il semble que JSON.NET est configuré un peu différemment entre l'utilisation de WebApi et son utilisation autonome.

Le sérialiseur Json.NET définit par défaut IgnoreSerializableAttribute sur true. Dans WebAPI, nous définissons cela sur false.

Donc, étant donné que vous ne pouvez pas prendre le [SerializableAttribute] off (car il ne fait pas partie de votre code), vous pouvez essayer de modifier le paramètre par défaut WebApi JSON.NET pour l'ignorer en utilisant cette réponse ici :

((DefaultContractResolver)config.Formatters.JsonFormatter.SerializerSettings.ContractResolver).IgnoreSerializableAttribute = true;

Peut-être aussi envisager d'utiliser un DTO pour que vos résultats soient envoyés dans la réponse - cela vous donnera un contrôle total sur l'objet envoyé sur le câble.

7
Mark Jones

Cela corrige l'erreur JSON, bonne chance avec XML. Ajoutez ceci à la classe WebApiConfig sous la méthode Register.

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

METTRE À JOUR:

J'ai trouvé deux solutions à cela. Le premier et le plus simple à implémenter consiste à modifier les IEnumerables, ICollections en un type de List. La 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 est de ne pas utiliser le sérialiseur JSON natif, la première méthode que j'ai publiée fonctionne toujours.

9
Ray Suelzer

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 des propriétés de navigation telles que IList Locations, je viens de créer l'entité viewmapper/dto par-dessus pour retourner.

Cela fonctionne trouver 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;}
}
3
aamir sajjad