Не удалось сериализовать тело ответа для типа содержимого
Я создаю приложение MVC4, которое использует как контроллеры, так и ApiControllers. Я изменил маршрут веб-API по умолчанию, чтобы включить имена действий. Когда я пытаюсь получить список тестов, я получаю сообщение об ошибке:
Тип объекта ObjectContent`1 не смог сериализовать тело ответа для тип контента 'application/json; кодировка = UTF-8'
InnerException это (я возвращаю JSON в этом случае, то же самое происходит с 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)"
}
Это код, который я запускаю:
// 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;
}
И это модель 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;
}
}
Я не совсем уверен, где проблема. Может ли это быть NHibernate Proxy (я использую Fluent NHibernate)? Странно то, что если я не использую ApiController и не возвращу JSON вручную, это будет отлично!
Update:
В соответствии с приведенным ниже ответом, это код, который я должен был добавить в Application_Start():
HttpConfiguration config = GlobalConfiguration.Configuration;
((DefaultContractResolver)config.Formatters.JsonFormatter.SerializerSettings.ContractResolver).IgnoreSerializableAttribute = true;
Ответы
Ответ 1
IdentityEqualityCompairer
в структуре NHibernate, как представляется, имеет аннотацию [SerializableAttribute]
.
Взято из комментария на ответе здесь Почему веб-API не десериализует это, а JSON.Net будет? похоже, что JSON.NET настроен несколько иначе между использованием WebApi и его автономным использованием.
Серийный анализатор Json.NET по умолчанию устанавливает для параметра IgnoreSerializableAttribute значение true. В WebAPI мы устанавливаем значение false.
Итак, вы не можете отключить [SerializableAttribute]
(так как это не входит в ваш код), вы можете попробовать изменить настройку по умолчанию для WebApi JSON.NET, чтобы игнорировать его с помощью этого ответа здесь:
((DefaultContractResolver)config.Formatters.JsonFormatter.SerializerSettings.ContractResolver).IgnoreSerializableAttribute = true;
Возможно, также рассмотрите возможность использования DTO для отправки результатов в ответ - это даст вам полный контроль над объектом, отправляемым по проводу.
Ответ 2
Это исправляет ошибку JSON, удачи в XML. Добавьте это в класс WebApiConfig в методе Register.
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling=Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
UPDATE:
Я нашел два решения. Первым и самым простым в реализации является изменение любых IEnumerables, ICollections для типа List. WebAPI может сериализовать эти объекты, однако он не может сериализовать типы интерфейсов.
public class Store
{
[StringLength(5)]
public string Zip5 { get; set; }
public virtual List<StoreReport> StoreReports { get; set; } //use a list here
}
Другой вариант заключается в том, чтобы не использовать собственный сериализатор JSON, первый метод, который я опубликовал, все еще работает.
Ответ 3
hmmm, После этого может помочь.
Я получал такое же исключение, и в моем случае я сначала передавал фактический объект poco, созданный для кода сущности. Поскольку он содержит свойства навигации, такие как IList Locations, я просто создал объект viewmapper/dto поверх него, чтобы вернуться.
Теперь он работает.
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;}
}