Не удалось сериализовать ответ в веб-API с помощью Json


109

Я работаю с ASP.NET MVC 5 Web Api. Я хочу проконсультироваться со всеми моими пользователями.

Я написал api/usersи получаю это:

«Типу 'ObjectContent`1' не удалось сериализовать тело ответа для типа контента 'application / json; charset = utf-8'»

В WebApiConfig я уже добавил эти строки:

HttpConfiguration config = new HttpConfiguration();
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

Но все равно не работает.

Моя функция для возвращаемых данных такова:

public IEnumerable<User> GetAll()
{
    using (Database db = new Database())
    {
        return db.Users.ToList();
    }
}

Как выглядит объект значения, который вы пытаетесь передать потребителю?
— mckeejm

Спасибо! Просто к сведению - я думаю, это следует читать: using (Database db = new Database ()) {List <UserModel> listOfUsers = new List <UserModel> (); foreach (пользователь var в db.Users) {UserModel userModel = new UserModel (); userModel.FirstName = user.FirstName; userModel.LastName = user.LastName; listOfUsers.Add (userModel); } IEnumerable <UserModel> users = listOfUsers; вернуть пользователей; } .. поскольку все результаты возвращали одинаковые значения.
— Джаред Уиттингтон

Ответы:


76

Когда дело доходит до возврата данных потребителю из Web Api (или любой другой веб-службы, если на то пошло), я настоятельно рекомендую не передавать обратно объекты, поступающие из базы данных. Гораздо надежнее и удобнее использовать модели, в которых вы можете контролировать, как выглядят данные, а не базу данных. Таким образом, вам не придется так много возиться с форматерами в WebApiConfig. Вы можете просто создать UserModel с дочерними моделями в качестве свойств и избавиться от циклов ссылок в возвращаемых объектах. Это делает сериализатор намного счастливее.

Кроме того, обычно нет необходимости удалять средства форматирования или поддерживаемые типы мультимедиа, если вы просто указываете заголовок «Accepts» в запросе. Игра с этим иногда может запутать вас еще больше.

Пример:

public class UserModel {
    public string Name {get;set;}
    public string Age {get;set;}
    // Other properties here that do not reference another UserModel class.
}

Когда вы говорите о моделях, вы хотите сказать, что я делаю? Вернуть IEnumerable пользователей, которые являются моделью.
— CampDev

5
Вы возвращаете объект. Сущность относится к объекту в БД, который можно получить по уникальному идентификатору. Вы возвращаете все объекты User из своей базы данных. Я предлагаю вам создать новый класс под названием «UserModel» и для каждого объекта User, который вы получаете из базы данных, создать новый экземпляр класса модели данных, заполненный необходимой информацией, которую вы хотите раскрыть. Возвращает IEnumerable объектов UserModel в отличие от сущностей User. Убедитесь, что свойства модели не относятся к экземплярам класса UserModel. Вот что втягивает вас в эту проблему.
— jensendp

3
ncampuzano верен, вы не хотите возвращать автоматически сгенерированные объекты. Если бы вы использовали хранимые процедуры для доступа к базе данных, разделение было бы более четким. Вам нужно было бы создать объект значения C # и сопоставить значения из IDataReader с объектом значения. Поскольку вы используете EF, для вас создаются классы, но это специальные классы EF, которые знают больше, чем объекты значений. Вы должны возвращать своему клиенту только «тупые» объекты значений.
— mckeejm

1
@Donny Если вы используете DBContext или репозиторий в своем контроллере, который возвращает объекты обратно из БД, вы можете просто сопоставить объекты с моделями (например, DTO) в контроллере ... но я предпочитаю иметь Контроллер вызывает службу, которая возвращает модель / DTO. Обратите внимание на AutoMapper - отличный инструмент для обработки карт.
— Бен

1
@NH. Вы можете использовать вышеупомянутые махинации, но всему есть свое место. «Сущности», предоставляемые доступом к уровню данных, обычно должны оставаться на уровне данных. Все, что хочет использовать эти данные на бизнес-уровне приложения, обычно также будет использовать «сущности» в преобразованной форме (объекты домена). И тогда данные, которые возвращаются и вводятся пользователем, обычно также будут другой формой (модели). Согласны, что это может быть утомительно везде выполнять такие преобразования, но именно здесь такие инструменты, как AutoMapper, действительно пригодятся.
— jensendp

147

Если вы работаете с EF, помимо добавления кода ниже на Global.asax

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
    .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters
    .Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);          

Не забудьте импортировать

using System.Data.Entity;

Затем вы можете вернуть свои собственные модели EF

Просто как тот!


Даже если это может помочь для EF, решение не относится к EF и работает также с другими типами моделей. Использование Global.asax не представляется необходимым. Это было предназначено для контроллеров?
— Matthieu

16
Приветствуются некоторые объяснения того, что делает этот код и его последствия.
— Джейкоб

1
Спасибо, что столкнулся с аналогичной проблемой, этот ответ помог мне решить проблему.
— RK_Aus

Меня устраивает. Не нужно добавлять using System.Data.Entity; в global.asax. Спасибо.
— Доктор МАФ

Оно работает. Просто добавьте код выше на Global.asax, вот и все, нет необходимости импортировать с помощью System.Data.Entity;
— Хемант Рамфул 01

52

Правильный ответ - это один из возможных вариантов, однако это перебор, когда вы можете исправить это с помощью одной настройки конфигурации.

Лучше использовать его в конструкторе dbcontext

public DbContext() // dbcontext constructor
            : base("name=ConnectionStringNameFromWebConfig")
{
     this.Configuration.LazyLoadingEnabled = false;
     this.Configuration.ProxyCreationEnabled = false;
}

Ошибка веб-API Asp.Net: типу ObjectContent`1 не удалось сериализовать тело ответа для типа контента application / xml; charset = utf-8 '


ваш код будет удален, если мы обновим модель из базы данных.
— Bimal Das

1
Вы можете легко разделить его, удалив файлы .tt и имея несвязанный контекст. Каждый раз, когда вы создаете модель, просто добавляйте новый класс на место. @Brimal: Вы можете следить за этим youtube.com/watch?v=yex0Z6qwe7A
— Md. Alim Ul Karim

1
Чтобы избежать перезаписи, вы можете отключить отложенную загрузку в свойствах edmx. У меня это сработало.
— Francisco G

@FranciscoG работает, но теряется, если мы удалим edmx и регенерируем.
— Md. Alim Ul Karim

1
@BimalDas попробуйте это youtube.com/… . Он не удалится
— М. Алим Уль Карим

37

Добавьте этот код global.asaxниже Application_Start:

Обновить с .Ignoreна .Serialize. Это должно сработать.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
            GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

1
Отлично работает, спасибо! Не могли бы вы лучше объяснить, почему нам нужно удалить Xml Formatter, чтобы он работал?
— Jav_1

Нет необходимости добавлять сериализатор json (по крайней мере, в моем случае), но необходимо было удалить форматирование Xml. Я предполагаю, что сериализатор xml не может сериализовать анонимные типы, и, удалив его, результат сериализуется как json. Если моя догадка верна, можно будет получить данные из контроллера, запросив тип MIME «application / json».
— LosManos,

11
public class UserController : ApiController
{

   Database db = new Database();

   // construction
   public UserController()
   {
      // Add the following code
      // problem will be solved
      db.Configuration.ProxyCreationEnabled = false;
   }

   public IEnumerable<User> GetAll()
    {
            return db.Users.ToList();
    }
}

Вау, у меня это сработало. Но почему? Что делает свойство ProxyCreationEnabled?
— jacktric

работал со мной, но что это за код? Я также отметил, что все подклассы извлекаются с нулевым значением !!
— Waleed A. Elgalil

10

Мне не нравится этот код:

foreach(var user in db.Users)

В качестве альтернативы можно было бы сделать что-то вроде этого, что сработало для меня:

var listOfUsers = db.Users.Select(r => new UserModel
                         {
                             userModel.FirstName = r.FirstName;
                             userModel.LastName = r.LastName;

                         });

return listOfUsers.ToList();

Однако в итоге я использовал решение Лукаса Роселли.

Обновление: упрощено за счет возврата анонимного объекта:

var listOfUsers = db.Users.Select(r => new 
                         {
                             FirstName = r.FirstName;
                             LastName = r.LastName;
                         });

return listOfUsers.ToList();

10

Я решил это, используя этот код в файле WebApiConfig.cs

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

большое спасибо. не знаю, что это делает с безопасностью.
— Arun Prasad ES

6

Также существует сценарий, который генерирует такую ​​же ошибку:

В случае возврата List<dynamic> к методу веб-API

Пример:

public HttpResponseMessage Get()
{
    var item = new List<dynamic> { new TestClass { Name = "Ale", Age = 30 } };

    return Request.CreateResponse(HttpStatusCode.OK, item);
}

public class TestClass
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Итак, для этого сценария используйте [KnownTypeAttribute] в возвращаемом классе (все они) следующим образом:

[KnownTypeAttribute(typeof(TestClass))]
public class TestClass
{
    public string Name { get; set; }
    public int Age { get; set; }
}

У меня это работает!


Что делать, если вы используете linq pivot с динамическими столбцами?
— codegrid

[KnownTypeAttribute (typeof (TestClass))] решил мою проблему
— Guillaume Raymond

6

Добавление этого в свой Application_Start()метод Global.asaxфайла должно решить проблему

protected void Application_Start()
{
    GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
        .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    GlobalConfiguration.Configuration.Formatters
        .Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter); 
// ...
}

МЕТОД 2: [Не рекомендуется]
Если вы работаете с EntityFramework, вы можете отключить прокси в конструкторе класса DbContext. ПРИМЕЧАНИЕ: этот код будет удален, если вы обновите модель.

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.ProxyCreationEnabled = false;
  }
}

1
Спасибо, Суман. У меня была такая же проблема. Я тестировал свой веб-API и столкнулся с этой проблемой. ваше решение решает проблему. большое спасибо.
— TarakPrajapati

4

Мой личный фаворит: просто добавьте приведенный ниже код в App_Start/WebApiConfig.cs. Это вернет json вместо XML по умолчанию, а также предотвратит возникшую ошибку. Не нужно редактировать, Global.asaxчтобы удалить XmlFormatterи т. Д.

Типу 'ObjectContent`1' не удалось сериализовать тело ответа для типа содержимого 'application / xml; кодировка = UTF-8

config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

2

Используйте AutoMapper ...

public IEnumerable<User> GetAll()
    {
        using (Database db = new Database())
        {
            var users = AutoMapper.Mapper.DynamicMap<List<User>>(db.Users);
            return users;
        }
    }

2

Используйте следующее пространство имен:

using System.Web.OData;

Вместо того :

using System.Web.Http.OData;

Это сработало для меня


2

Добавьте строку ниже

this.Configuration.ProxyCreationEnabled = false;

Два способа использования ProxyCreationEnabledas false.

  1. Добавьте его в DBContextконструктор

    public ProductEntities() : base("name=ProductEntities")
    {
        this.Configuration.ProxyCreationEnabled = false;
    }

ИЛИ

  1. Добавьте строку внутри Getметода

    public IEnumerable<Brand_Details> Get()
    {
        using (ProductEntities obj = new ProductEntities())
        {
            this.Configuration.ProxyCreationEnabled = false;
            return obj.Brand_Details.ToList();
        }
    }

2

Решение, которое сработало для меня:

  1. Используйте [DataContract]для класса и [DataMember]атрибутов для каждого сериализуемого свойства. Этого достаточно, чтобы получить результат Json (например, от скрипачей).

  2. Чтобы получить сериализацию xml, напишите в Global.asaxэтом коде:

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; xml.UseXmlSerializer = true;

  3. Прочтите эту статью, она помогла мне понять сериализацию: https://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization

1

Чтобы добавить к ответу jensendp:

Я бы передал объект в созданную пользователем модель и использовал значения из этого объекта для установки значений в вашей вновь созданной модели. Например:

public class UserInformation {
   public string Name { get; set; }
   public int Age { get; set; }

   public UserInformation(UserEntity user) {
      this.Name = user.name;
      this.Age = user.age;
   }
}

Затем измените тип возвращаемого значения на: IEnumerable<UserInformation>


1
есть более элегантные способы сделать перевод для вас, так что вам не нужно поддерживать каждое свойство .. AutoMapper и ValueInjecter - два примечательные из них
— Sonic Soul

1

Это моя ошибка

Я в основном добавляю одну строчку, в которой они

  • entity.Configuration.ProxyCreationEnabled = false;

в UsersController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using UserDataAccess;

namespace SBPMS.Controllers
{
    public class UsersController : ApiController
    {


        public IEnumerable<User> Get() {
            using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
                entities.Configuration.ProxyCreationEnabled = false;
                return entities.Users.ToList();
            }
        }
        public User Get(int id) {
            using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
                entities.Configuration.ProxyCreationEnabled = false;
                return entities.Users.FirstOrDefault(e => e.user_ID == id);
            }
        }
    }
}

Вот мой результат:


1

Используйте [Serializable] для класса:

Пример:

[Serializable]
public class UserModel {
    public string Name {get;set;}
    public string Age {get;set;}
}

У меня это сработало!


1
Выражаясь таким образом, можно было почти подумать, что каждый, кто раньше отвечал на этот пост, был тупым;)… Ну, я серьезно сомневаюсь, что да, так что, вероятно, это означает, что это решение было просто неприменимо, когда был задан вопрос, еще в 2015 году ... Я сам мало что знаю об этом синтаксисе, но мне кажется, что либо он относительно новый, либо в нем могут быть некоторые недостатки, которые делают его непригодным для использования в определенных случаях использования. У меня такое чувство, что ваше решение может быть полезно для будущих читателей, задающих этот вопрос, но оно, безусловно, поможет, если вы проясните его ограничения.
— jwatkins

1

Вам нужно будет определить Serializer Formatter в WebApiConfig.cs, доступном в папке App_Start, например

Добавление config.Formatters.Remove (config.Formatters.XmlFormatter); // который предоставит вам данные в формате JSON

Добавление config.Formatters.Remove (config.Formatters.JsonFormatter); // который предоставит вам данные в формате XML


Вы заслуживаете медали.
— TheKrogrammer

1

Просто введите следующие строки в global.asax:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;  
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

Импортировать

using System.Data.Entity;

0

Еще один случай, когда я получил эту ошибку, - это когда мой запрос к базе данных возвратил нулевое значение, но мой тип модели пользователь / представление был установлен как не допускающий значения NULL. Например, изменение моего поля UserModel с intна int?разрешенное.


0

Это также происходит, когда Response-Type не является общедоступным! Я вернул внутренний класс, поскольку использовал Visual Studio для создания типа.

internal class --> public class

0

Хотя все приведенные выше ответы верны, можно проверить InnerException> ExceptionMessage .

Если он говорит что-то вроде этого: « Экземпляр ObjectContext удален и больше не может использоваться для операций, требующих подключения. ». Это может быть проблемой из-за поведения EF по умолчанию.

Назначая LazyLoadingEnabled = ложь в конструкторе DbContext будет делать трюк.

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.LazyLoadingEnabled = false;
  }
}

Для получения более подробной информации о поведении EagerLoading и LazyLoading EF обратитесь к этой статье MSDN .


0

В моем случае у меня было аналогичное сообщение об ошибке:

Типу 'ObjectContent`1' не удалось сериализовать тело ответа для типа содержимого 'application / xml; charset = utf-8 '.

Но когда я копнул глубже, проблема была в следующем:

Введите name.SomeSubRootType с именем контракта данных SomeSubRootType: //schemas.datacontract.org/2004/07/WhatEverService не ожидается. Рассмотрите возможность использования DataContractResolver, если вы используете DataContractSerializer, или добавьте какие-либо типы, не известные статически, в список известных типов - например, с помощью атрибута KnownTypeAttribute или путем добавления их в список известных типов, передаваемых в сериализатор.

Способ, которым я решил, добавив KnownType.

[KnownType(typeof(SomeSubRootType))]
public partial class SomeRootStructureType

Это было решено на основе этого ответа .

Ссылка: https://msdn.microsoft.com/en-us/library/ms730167(v=vs.100).aspx


0

Visual Studio 2017 или 2019 совершенно не думают об этом, потому что сама Visual Studio требует, чтобы вывод был в формате json , в то время как формат Visual Studio по умолчанию - « XmlFormat» (config.Formatters.XmlFormatter) .

Visual Studio должна делать это автоматически, чтобы не доставлять разработчикам столько хлопот.

Чтобы исправить эту проблему, перейдите в файл WebApiConfig.cs и добавьте

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

после " config.MapHttpAttributeRoutes (); " в методе Register (конфигурация HttpConfiguration) . Это позволит вашему проекту производить вывод в формате json.


0

В моем случае я решил воссоздать базу данных. Я внес некоторые изменения в модель и, запустив Update-Database в консоли диспетчера пакетов, получил следующую ошибку:

«Оператор ALTER TABLE конфликтует с ограничением FOREIGN KEY« FK_dbo.Activities_dbo.Projects_ProjectId ». Конфликт произошел в базе данных« TrackEmAllContext-20190530144302 », таблица« dbo.Projects », столбец« Id »».


0

В случае: если добавление кода в WebApiConfig.cs или Global.asax.cs у вас не работает:

.ToList();

Добавить функцию .ToList ().

Я опробовал все решения, но у меня сработало следующее:

var allShops = context.shops.Where(s => s.city_id == id)**.ToList()**;
return allShops;

Я надеюсь, что это помогает.


0

в моем случае это было исправлено, когда я удалил ключевое слово virtual перед своими свойствами навигации, я имею в виду справочные таблицы. поэтому я изменил

public virtual MembershipType MembershipType { get; set; }

кому:

public MembershipType MembershipType { get; set; }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.