16

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;
Astaar
  • 5,858
  • 8
  • 40
  • 57

3 Answers3

9

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.

Ray Suelzer
  • 4,026
  • 5
  • 39
  • 55
  • 1
    +1 this has driven me up the wall ALL day, this was the answer that made it all happen. thank you so much!! I also added the `[JsonIgnore]` where required too – jim tollan Sep 12 '13 at 20:23
  • I was tying to use a ViewModel that's not tracked by entity framework in my web api and this solution successfully serialized my new type – Dan Beaulieu Aug 20 '15 at 19:21
7

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.

Community
  • 1
  • 1
Mark Jones
  • 12,156
  • 2
  • 50
  • 62
  • Fixed the issue indeed! Now I'm running into another problem because of a function is not implemented by NHibernate, but that is a different topic. – Astaar Dec 30 '12 at 14:29
  • 1
    Just add that code to `WebApiConfig.cs`? And why is `NHibernate` involved here? – Blaise Aug 15 '13 at 16:10
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;}
}
aamir sajjad
  • 3,019
  • 1
  • 27
  • 26
  • 3
    Removing an entire relationship from the class is hardly a solution. – Ray Suelzer Mar 03 '13 at 06:35
  • @RaySülzer it works for me in that particular context. May be you have different problem. however, i set an another perspective to see the problem. May be somebody can get the benefit from it. why did you down voted it. i can send you the example/demo to prove that. so please, don't vote it, instead of down voting – aamir sajjad Mar 03 '13 at 06:38
  • But you are returning an entirely different class... Why not just remove the public IList from the original class? People are having this issue because they need to serialize ICollections using Enity Framework and Microsoft has shamefully ignored this need. – Ray Suelzer Mar 03 '13 at 06:46
  • well, this is just for example. whenever you change the ef poco entity. what will you name it a dto, viewmapper or what you want to name it. since, if you remove the navigation property from ef poco. it will break the entity association. so the idea is to keep the domain design as it is, and get only required information. – aamir sajjad Mar 03 '13 at 06:50