Failed to serialize the response body for content

2020-01-29 10:06发布

问题:

I'm building a MVC4 application that uses both Controllers and ApiControllers. I modified the default Web API route to include action names. When I try to get a list of Benchmarks, I'm getting this error message:

The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'

The InnerException is this (I'm returning JSON in that case, same happens with 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)"
}

This is the code that I run:

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

And this is the Benchmark model:

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

I'm not quite sure where the problem lies. Could it be the NHibernate Proxy (I use Fluent NHibernate)? The odd thing is that if I don't use an ApiController, and manually return JSON, this works just perfectly!

Update:

As per the answer below, this is the code I had to add in Application_Start() :

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

回答1:

The IdentityEqualityCompairer in the NHibernate framework appears to have the [SerializableAttribute] annotation.

Taken from a comment on an answer here Why won't Web API deserialize this but JSON.Net will? it looks like JSON.NET is configured a little differently between WebApi usage and it's standalone usage.

Json.NET serializer by default set the IgnoreSerializableAttribute to true. In WebAPI, we set that to false.

So given you cannot take the [SerializableAttribute] off (as it is not within your code) you could try changing the default WebApi JSON.NET setting to ignore it using this answer here:

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

Perhaps also consider Using a DTO for your results being sent over in the response - this will give you full control over the object being sent over the wire.



回答2:

This fixes the JSON error, good luck with XML. Add this to the WebApiConfig class under the Register Method.

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

UPDATE:

I found two solutions to this. The first and easiest to implement is to change any IEnumerables, ICollections to a type of List. The WebAPI can serialize this objects, it however cannot serialize interface types.

public class Store
{

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

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

The other option is to not use the native JSON serializer the first method I posted still works.



回答3:

hmmm, Following may help.

I was getting the same exception, and in my case I was passing the actual poco entity created for entity code first. Since, it contains navigation properties such as IList Locations, I just created the viewmapper/dto entity on top of it to return.

It works find now.

Poco Entity:

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