Jsonを使用したWeb APIでの応答のシリアル化に失敗しました


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 2014

本当にありがとう!ただfyi-私はそれを読むべきだと思います:using(Database db = new Database()){List <UserModel> listOfUsers = new List <UserModel>(); foreach(var user in db.Users){UserModel userModel = new UserModel(); userModel.FirstName = user.FirstName; userModel.LastName = user.LastName; listOfUsers.Add(userModel); IEnumerable <UserModel> users = listOfUsers; ユーザーを返す; 結果はすべて同じ値を返していました。
Jared Whittington、2015

回答:


76

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を返します。
CampDev 2014

5
エンティティを返しています。エンティティは、一意のIDで取得できるDB内のオブジェクトを指します。データベースからすべてのユーザーエンティティを返します。「UserModel」という新しいクラスを作成し、データベースから取得する各ユーザーエンティティに対して、公開する必要のある情報が入力されたデータモデルクラスの新しいインスタンスを作成することをお勧めします。Userエンティティではなく、UserModelオブジェクトのIEnumerableを返します。モデルのプロパティがUserModelクラスのインスタンスを参照していないことを確認してください。それがあなたをこの問題に陥らせているのです。
jensendp 2014

3
ncampuzanoは正しいです。自動生成されたエンティティを返したくありません。データベースアクセスにストアドプロシージャを使用している場合、分離はより明確になります。C#値オブジェクトを生成し、IDataReaderから値オブジェクトに値をマップする必要があります。EFを使用しているため、生成されるクラスがありますが、これらは、値オブジェクトよりも多くのことを知っている特別なEFクラスです。「ダム」値オブジェクトのみをクライアントに返す必要があります。
mckeejm 2014

1
@Donny DBからエンティティを返すコントローラーでDBContextまたはリポジトリを使用している場合、オブジェクトをコントローラーのモデル(たとえば、DTO)にマップするだけでよいのですが...コントローラはモデル/ DTOを返すサービスを呼び出します。AutoMapper-マッピングを処理するための優れたツールを確認してください。
2015年

1
@NH。前述のシェニガンは絶対に使用できますが、すべての場所があります。データレイヤーへのアクセスによって提供される「エンティティ」は、通常、データレイヤー内に留まります。アプリケーションのビジネス層内でこのデータを使用したい場合は、通常、変換された形式の「エンティティ」も使用します(ドメインオブジェクト)。そして、ユーザーに返され、ユーザーから入力されるデータも、通常は別のフォーム(モデル)になります。この種の変換を至る所で行うのは退屈な作業になる可能性があることに同意しますが、AutoMapperのようなツールが実際に役立つのはそのためです。
jensendp 2017年

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 2016年

16
このコードの機能とその意味についての説明を歓迎します。
ジェイコブ

1
私が同様の問題に直面していたことに感謝します。この答えは問題を解決するのに役立ちました。
RK_Aus 2017年

わたしにはできる。System.Data.Entityを使用して追加する必要はありません。global.asaxに。ありがとうございました。
MAF博士、

できます。上記のGlobal.asaxにコードを追加するだけです。これですべてです。System.Data.Entityを使用してインポートする必要はありません。
Hemant Ramphul

52

正しい答えを与えることは1つの方法ですが、1つの構成設定で修正できる場合はやりすぎです。

dbcontextコンストラクタで使用する方が良い

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

Asp.Net Web APIエラー: 'ObjectContent`1'タイプは、コンテンツタイプ 'application / xml;の応答本文のシリアル化に失敗しました。charset = utf-8 '


データベースからモデルを更新すると、コードが削除されます。
Bimal Das 2016

1
.ttファイルを削除することで簡単に分離でき、コンテキストがばらばらになります。モデルを生成するたびに、その場所に新しいクラスを追加するだけです。@Brimal:このyoutube.com/watch?v=yex0Z6qwe7A
Ul Karim

1
上書きされないようにするには、edmxプロパティから遅延読み込みを無効にします。それは私のために働いた。
Francisco G

@FranciscoG動作しますが、edmxを削除して再生成すると失われます。
Md。Alim Ul Karim

1
@BimalDasがこのyoutube.com/…を試してください。削除されません
Md。Alim Ul Karim 2016

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プロパティは何をしますか?
ジャックトリック2017年

私と一緒に働いたが、このコードは何ですか?また、すべてのサブクラスがnullで取得されたことにも注意しました!!
Waleed A. Elgalil 2017

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

しかし、結局、Lucas Roselliのソリューションを使用しました。

更新:匿名オブジェクトを返すことで簡素化:

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

どうもありがとう。ただし、これがセキュリティにどのような影響を与えるかはわかりません。
アルンプラサードES

6

同じエラーを生成するこのシナリオもあります:

リターンがList<dynamic>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ピボットを使用している場合はどうなりますか?
codegrid 2015年

[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
スーマンに感謝します。私は同じ問題を抱えていました。私は自分のWeb APIをテストしていて、この問題に悩まされていました。あなたの解決策は問題を解決します。どうもありがとう。
TarakPrajapati

4

私の個人的なお気に入り:以下のコードをに追加するだけApp_Start/WebApiConfig.csです。これにより、デフォルトではXMLではなくjsonが返され、発生したエラーも防止されます。Global.asax削除XmlFormatterなどを編集する必要はありません。

'ObjectContent`1タイプは、コンテンツタイプ' application / xml 'の応答本文のシリアル化に失敗しました。charset = 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;

ProxyCreationEnabledとして使用する2つの方法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は2つの注目すべきものです
Sonic Soul

1

これは私のエラーです

基本的には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
このように言い換えると、以前にこの投稿に回答した人は誰もが愚かだったと思う人がほとんどだと思います;)…まあ、私は彼らが正しかったのか真剣に疑っています。私はその構文について私自身はあまり知りませんが、それは比較的新しいものであるか、特定のユースケースで使用できないようにするいくつかの欠点があるかもしれないと感じています。私はあなたの解決策がこの質問に到達する将来の読者に役立つかもしれないと感じていますが、あなたがその限界を明確にしてくれるなら確かに助けになるでしょう。
jwatkins

1

次のようにApp_Startフォルダで利用可能なWebApiConfig.cs内でシリアライザフォーマッタを定義する必要があります

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

このエラーを受け取ったもう1つのケースは、データベースクエリがnull値を返したが、ユーザー/ビューモデルタイプがnull不可に設定された場合です。たとえば、UserModelフィールドintint?resolvedに変更します。


0

これは、Response-Typeがパブリックでない場合にも発生します。Visual Studioを使用して型を生成しているときに、内部クラスを返しました。

internal class --> public class

0

上記のすべての答えは正しいですが、InnerException> ExceptionMessageを確認することもできます

これが「ObjectContextインスタンスが破棄され、接続を必要とする操作に使用できなくなった」と言った場合。これは、EFのデフォルトの動作が原因で問題になる可能性があります。

DbContextコンストラクターでLazyLoadingEnabled = falseを割り当てることで、トリックが実行されます。

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

EFのEagerLoadingおよびLazyLoading動作の詳細については、このMSDNの記事を参照してください。


0

私の場合、同様のエラーメッセージが表示されました。

'ObjectContent`1タイプは、コンテンツタイプ' application / xml 'の応答本文のシリアル化に失敗しました。charset = utf-8 '。

しかし、さらに深く掘り下げると、問題は次のとおりでした。

タイプ 'name.SomeSubRootType'とデータコントラクト名 'SomeSubRootType://schemas.datacontract.org/2004/07/WhatEverService'は予期されていません。DataContractSerializerを使用している場合、または既知のタイプのリストに静的に認識されていないタイプを追加する場合は、DatantractResolverの使用を検討してください。たとえば、KnownTypeAttribute属性を使用するか、シリアライザに渡される既知のタイプのリストに追加します。

追加して解決した方法KnownType

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

これは、この回答に触発されて解決されまし

リファレンス:https : //msdn.microsoft.com/en-us/library/ms730167(v=vs.100).aspx


0

Visual Studio のデフォルトの形式は " XmlFormat"(config.Formatters.XmlFormatter)であるのに対し、Visual Studio自体は出力をjson形式にする必要があるため、Visual Studio 2017または2019はこれについてはまったく考えられません

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出力を生成できます。


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

私の場合、ナビゲーションプロパティの前に仮想キーワードを削除したときに修正されました。つまり、参照テーブルです。だから私は変わった

public virtual MembershipType MembershipType { get; set; }

に:

public MembershipType MembershipType { get; set; }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.