【问题标题】:Failed to serialize the response in Web API with Json无法使用 Json 序列化 Web API 中的响应
【发布时间】:2014-05-30 15:53:02
【问题描述】:

我正在使用 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();
    }
}

【问题讨论】:

  • 您试图传递给消费者的值对象是什么样的?
  • 非常感谢!仅供参考-我认为应该阅读: using (Database db = new Database ()) { List listOfUsers = new List(); foreach(var user in db.Users) { UserModel userModel = new UserModel(); userModel.FirstName = user.FirstName; userModel.LastName = 用户.LastName; listOfUsers.Add(userModel); } IEnumerable 用户 = listOfUsers;回头客; ..因为结果都返回相同的值。

标签: c# asp.net json asp.net-web-api


【解决方案1】:

当涉及从 Web Api(或任何其他 Web 服务)将数据返回给消费者时,我强烈建议不要将来自数据库的实体传回。使用可以控制数据外观而不是数据库的模型更加可靠和可维护。这样您就不必在 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。
  • 您正在返回一个实体。实体是指数据库中可以通过唯一 id 检索的对象。您正在从数据库中返回所有用户实体。我建议您创建一个名为“UserModel”的新类,并为您从数据库中获取的每个用户实体创建一个数据模型类的新实例,其中填充了您想要公开的必要信息。返回一个 IEnumerable 的 UserModel 对象,而不是 User 实体。确保模型属性不引用 UserModel 类的实例。这就是让你陷入这个问题的原因。
  • ncampuzano 是正确的,您不想返回自动生成的实体。如果您使用存储过程进行数据库访问,则分离会更加清晰。您需要生成一个 C# 值对象并将值从 IDataReader 映射到值对象。由于您使用的是 EF,因此会为您生成一些类,但这些是特殊的 EF 类,它们比值对象了解更多。您应该只将“哑”值对象返回给您的客户。
  • @Donny 如果您在控制器中使用 DBContext 或从数据库返回实体的存储库,那么您只需将对象映射到控制器中的模型(例如 DTO)。 .. 但我更喜欢让控制器调用返回模型/DTO 的服务。查看 AutoMapper - 处理映射的好工具。
  • @NH。您绝对可以使用上述恶作剧,但一切都有它的位置。访问数据层所提供的“实体”通常应该保留在数据层中。任何想要在应用程序的业务层中使用此数据的东西通常也会使用转换形式的“实体”(域对象)。然后返回给用户并从用户输入的数据通常也是另一种形式(模型)。同意在任何地方进行这种类型的转换可能会很乏味,但这就是 AutoMapper 等工具真正派上用场的地方。
【解决方案2】:

还有这种情况会产生同样的错误:

如果返回 List&lt;dynamic&gt; 到 web 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 与动态列一起使用会怎样?
  • [KnownTypeAttribute(typeof(TestClass))] 解决了我的问题
【解决方案3】:

如果您使用的是 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 中似乎不需要使用。它是为控制器设计的吗?
  • 欢迎对这段代码的作用及其含义进行一些解释。
  • 谢谢我遇到了类似的问题,这个答案帮助我解决了这个问题。
  • 它对我有用。无需使用 System.Data.Entity 添加;到 global.asax。谢谢。
  • 它有效。在 Global.asax 上面简单的添加代码,就是这样,不需要使用 System.Data.Entity 导入;
【解决方案4】:

我不喜欢这段代码:

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();

但是,我最终使用了 Lucas Roselli 的解决方案。

更新:通过返回匿名对象进行简化:

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

return listOfUsers.ToList();

【讨论】:

    【解决方案5】:

    添加到 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&lt;UserInformation&gt;

    【讨论】:

    • 有更优雅的方式为您处理翻译,这样您就不必维护每个属性.. AutoMapper 和 ValueInjecter 是两个值得注意的
    【解决方案6】:

    给出正确答案是一种方法,但是当您可以通过一个配置设置来修复它时,这是一种矫枉过正。

    最好在dbcontext构造函数中使用

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

    Asp.Net Web API Error: The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'

    【讨论】:

    • 如果我们从数据库更新模型,您的代码将被删除。
    • 您可以通过删除 .tt 文件轻松将其分开,并具有不相交的上下文。每次生成模型时,只需在该位置添加新类。 @Brimal:你可以关注这个youtube.com/watch?v=yex0Z6qwe7A
    • 为避免被覆盖,您可以从 edmx 属性禁用延迟加载。它对我有用。
    • @FranciscoG 它可以工作,但如果我们删除 edmx 并重新生成它就会丢失。
    • @BimalDas 试试这个youtube.com/…。它不会删除
    【解决方案7】:

    使用 AutoMapper...

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

    【讨论】:

      【解决方案8】:

      我收到此错误的另一种情况是当我的数据库查询返回空值但我的用户/视图模型类型设置为不可为空时。例如,将我的 UserModel 字段从 int 更改为 int? 已解决。

      【讨论】:

        【解决方案9】:

        将此代码添加到global.asax下面的Application_Start:

        从 .Ignore 更新为 .Serialize 。它必须有效。

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

        【讨论】:

        • 非常感谢!,您能否更好地解释一下为什么我们必须删除 Xml 格式化程序才能使其正常工作?
        • 不需要添加 json 序列化程序(至少在我的情况下),但需要删除 Xml 格式。我猜 xml 序列化程序无法序列化匿名类型,通过删除它,结果被序列化为 json。如果我的猜测是正确的,可以通过询问 MIME 类型“application/json”来从控制器中获取数据。
        【解决方案10】:

        我个人最喜欢的:只需将下面的代码添加到App_Start/WebApiConfig.cs。默认情况下,这将返回 json 而不是 XML,并且还可以防止您遇到的错误。无需编辑Global.asax 即可删除XmlFormatter 等。

        “ObjectContent`1”类型未能序列化内容类型“application/xml;”的响应正文;字符集=utf-8

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

        【讨论】:

          【解决方案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 属性有什么作用?
          • 和我一起工作,但这段代码是什么?我还注意到所有用 null 检索的子类!
          【解决方案12】:

          使用以下命名空间:

          using System.Web.OData;

          而不是:

          using System.Web.Http.OData;

          它对我有用

          【讨论】:

            【解决方案13】:

            对我有用的解决方案:

            1. 将[DataContract] 用于类,将[DataMember] 属性用于要序列化的每个属性。这足以获得 Json 结果(例如来自 fiddler)。

            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

            【讨论】:

              【解决方案14】:

              当 Response-Type 不公开时也会发生这种情况! 当我使用 Visual Studio 生成类型时,我返回了一个内部类。

              internal class --> public class
              

              【讨论】:

                【解决方案15】:

                虽然以上所有这些答案都是正确的,但可能需要检查 InnerException > ExceptionMessage。

                如果它说“T他 ObjectContext 实例已被释放,不能再用于需要连接的操作。”。由于 EF 的默认行为,这可能是一个问题。

                通过在您的 DbContext 构造函数中分配 LazyLoadingEnabled = false 即可解决问题。

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

                有关 EF 的 EagerLoading 和 LazyLoading 行为的更多详细信息,请参阅MSDN Article。

                【讨论】:

                  【解决方案16】:

                  在Global.asax 文件的Application_Start() 方法中添加这个应该可以解决问题

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

                  【讨论】:

                  • 谢谢苏曼。我遇到了同样的问题。我正在测试我的 Web API 并遇到了这个问题。您的解决方案解决了问题。非常感谢。
                  【解决方案17】:

                  在我的情况下,我收到了类似的错误消息:

                  “ObjectContent`1”类型未能序列化响应正文 内容类型'应用程序/xml; charset=utf-8'。

                  但是当我深入研究它时,问题是:

                  键入'name.SomeSubRootType' 带有数据合同名称 'SomeSubRootType://schemas.datacontract.org/2004/07/WhatEverService' 预计不会。如果您是,请考虑使用 DataContractResolver 使用 DataContractSerializer 或添加任何静态未知的类型 已知类型的列表 - 例如,通过使用 KnownTypeAttribute 属性或通过将它们添加到传递给 序列化器。

                  我通过添加KnownType解决的方式。

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

                  这是受answer启发而解决的。

                  参考:https://msdn.microsoft.com/en-us/library/ms730167(v=vs.100).aspx

                  【讨论】:

                    【解决方案18】:

                    我使用此代码将其解析为 WebApiConfig.cs 文件

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

                    【讨论】:

                    • 非常感谢。不过不知道这对安全性有什么影响。
                    【解决方案19】:

                    我基本上加了一行

                    • entities.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);
                                }
                            }
                        }
                    }
                    

                    【讨论】:

                      【解决方案20】:

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

                      在 Register(HttpConfiguration config) 方法中的“config.MapHttpAttributeRoutes();”之后。这将允许您的项目生成 json 输出。

                      【讨论】:

                        【解决方案21】:

                        就我而言,我解决了重新创建数据库的问题。 我对模型进行了一些更改并在包管理器控制台中启动更新数据库我收到以下错误:

                        “ALTER TABLE 语句与 FOREIGN KEY 约束“FK_dbo.Activities_dbo.Projects_ProjectId”冲突。冲突发生在数据库“TrackEmAllContext-20190530144302”、表“dbo.Projects”、列“Id”中。”

                        【讨论】:

                          【解决方案22】:

                          如果:如果将代码添加到 WebApiConfig.cs 或 Global.asax.cs 对您不起作用:

                          .ToList();
                          

                          添加 .ToList() 函数。

                          我尝试了所有解决方案,但以下对我有用:

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

                          我希望,它会有所帮助。

                          【讨论】:

                            【解决方案23】:

                            添加下面一行

                            this.Configuration.ProxyCreationEnabled = false;
                            

                            将ProxyCreationEnabled 用作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();
                                  }
                              }
                              

                            【讨论】:

                              【解决方案24】:

                              对类使用 [Serializable]:

                              例子:

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

                              它对我有用!

                              【讨论】:

                              • 这样说,几乎可以认为之前回答这个帖子的每个人都是愚蠢的;)......好吧,我严重怀疑他们是,所以这可能意味着这个解决方案根本不适用,当早在 2015 年就有人问过这个问题……我自己对这种语法知之甚少,但我觉得它要么相对较新,要么可能存在一些缺点,使其在某些用例中无法使用。我觉得您的解决方案可能对将来遇到此问题的读者有用,但如果您澄清其局限性,它肯定会有所帮助。
                              【解决方案25】:

                              您必须在 App_Start 文件夹中可用的 WebApiConfig.cs 中定义 Serializer Formatter,例如

                              添加 config.Formatters.Remove(config.Formatters.XmlFormatter); // 这将为您提供 JSON 格式的数据

                              添加 config.Formatters.Remove(config.Formatters.JsonFormatter); // 这将为您提供 XML 格式的数据

                              【讨论】:

                              • 你应该得到奖章。
                              【解决方案26】:

                              在我的情况下,当我在导航属性之前删除虚拟关键字时,它已得到修复, 我的意思是参考表。 所以我改变了

                              public virtual MembershipType MembershipType { get; set; }
                              

                              到:

                              public MembershipType MembershipType { get; set; }
                              

                              【讨论】:

                                【解决方案27】:

                                只需在 global.asax 中添加以下行:

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

                                导入

                                using System.Data.Entity;
                                

                                【讨论】:

                                  猜你喜欢
                                  • 2012-09-20
                                  • 1970-01-01
                                  • 2012-11-16
                                  • 1970-01-01
                                  • 2013-04-25
                                  • 2021-05-14
                                  相关资源
                                  最近更新 更多