Gagal untuk membuat serial respon di Web API dengan Json


109

Saya bekerja dengan ASP.NET MVC 5 Web Api. Saya ingin berkonsultasi dengan semua pengguna saya.

Saya menulis api/usersdan saya menerima ini:

"Jenis 'ObjectContent`1' gagal membuat serial isi respons untuk jenis konten 'application / json; charset = utf-8'"

Di WebApiConfig, saya sudah menambahkan baris ini:

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

Tapi tetap tidak berhasil.

Fungsi saya untuk mengembalikan data adalah ini:

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

Seperti apa objek nilai yang ingin Anda berikan kepada konsumen?
mckeejm

Terima kasih banyak! Hanya fyi - Saya pikir itu harus membaca: using (Database db = new Database ()) {List <UserModel> listOfUsers = new List <UserModel> (); foreach (var user di db.Users) {UserModel userModel = new UserModel (); userModel.FirstName = user.FirstName; userModel.LastName = user.LastName; listOfUsers.Add (userModel); } IEnumerable <UserModel> users = listOfUsers; pengguna kembali; } .. karena hasilnya semua mengembalikan nilai yang sama.
Jared Whittington

1
Kemungkinan duplikat gagal
menserialisasi

Jawaban:


76

Ketika datang untuk mengembalikan data kembali ke konsumen dari Web Api (atau layanan web lainnya dalam hal ini), saya sangat menyarankan untuk tidak mengirimkan kembali entitas yang berasal dari database. Jauh lebih andal dan mudah dipelihara untuk menggunakan Model di mana Anda memiliki kendali atas seperti apa datanya dan bukan database. Dengan begitu, Anda tidak perlu terlalu banyak mengutak-atik pemformat di WebApiConfig. Anda bisa membuat UserModel yang memiliki Model turunan sebagai properti dan menyingkirkan loop referensi di objek yang dikembalikan. Itu membuat pembuat serial lebih bahagia.

Selain itu, tidak perlu menghapus pemformat atau jenis media yang didukung biasanya jika Anda hanya menentukan header "Menerima" dalam permintaan. Bermain-main dengan barang-barang itu terkadang bisa membuat segalanya lebih membingungkan.

Contoh:

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

Saat Anda merujuk ke Model, Anda ingin mengatakan apa yang saya lakukan? Return IEnumerable of Users yang merupakan Model.
CampDev

5
Anda mengembalikan Entitas. Entitas mengacu pada objek di DB yang dapat diambil dengan id unik. Anda mengembalikan semua entitas Pengguna dari database Anda. Saya menyarankan agar Anda membuat kelas baru yang disebut "UserModel" dan untuk setiap entitas Pengguna yang Anda dapatkan dari database, buat instance baru kelas model data yang diisi dengan info yang diperlukan yang ingin Anda ungkapkan. Kembalinya IEnumerable objek UserModel sebagai lawan dari entitas Pengguna. Pastikan properti model tidak merujuk ke instance kelas UserModel. Itulah yang membawa Anda ke dalam masalah ini.
jensendp

3
ncampuzano benar, Anda tidak ingin mengembalikan entitas yang dibuat secara otomatis. Jika Anda menggunakan prosedur yang tersimpan untuk akses database, pemisahannya akan lebih jelas. Anda harus membuat objek nilai C # dan memetakan nilai dari IDataReader ke objek nilai. Karena Anda menggunakan EF, ada kelas-kelas yang dibuat untuk Anda, tetapi itu adalah kelas-kelas EF khusus yang tahu lebih banyak daripada yang dilakukan objek nilai. Anda hanya boleh mengembalikan objek bernilai "bodoh" ke klien Anda.
mckeejm

1
@Donny Jika Anda menggunakan DBContext atau Repositori di pengontrol yang mengembalikan entitas dari DB, maka Anda dapat memetakan objek ke model (misalnya DTO) di pengontrol ... tetapi saya lebih suka memiliki kontroler memanggil layanan yang mengembalikan model / DTO. Lihat AutoMapper - alat hebat untuk menangani pemetaan.
ben

1
@NH. Anda benar-benar dapat menggunakan shenanigans yang disebutkan di atas, tetapi semuanya memiliki tempatnya. "Entitas" yang disediakan oleh akses ke Lapisan Data biasanya harus tetap berada di Lapisan Data. Apa pun yang ingin menggunakan data ini dalam Lapisan Bisnis aplikasi biasanya akan menggunakan "entitas" dalam bentuk yang diubah juga (Objek Domain). Dan kemudian data yang dikembalikan ke dan input dari pengguna biasanya akan menjadi bentuk lain juga (Model). Setuju itu bisa membosankan untuk melakukan jenis transformasi ini di mana-mana, tapi di situlah alat seperti AutoMapper benar-benar berguna.
jensendp

147

Jika Anda bekerja dengan EF, selain menambahkan kode di bawah ini di Global.asax

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

Jangan lupa untuk mengimpor

using System.Data.Entity;

Kemudian Anda dapat mengembalikan Model EF Anda sendiri

Sederhana seperti itu!


Meskipun mungkin membantu EF, solusinya tidak spesifik untuk EF, dan juga dapat digunakan dengan model lain. Penggunaan tampaknya tidak perlu di Global.asax. Apakah itu ditujukan untuk pengontrol?
Matthieu

16
Beberapa penjelasan tentang apa yang dilakukan kode ini dan implikasinya akan disambut baik.
Jacob

1
Terima kasih saya menghadapi masalah yang sama, jawaban ini membantu saya menyelesaikan masalah.
RK_Aus

Ini bekerja untuk saya. Tidak perlu menambahkan menggunakan System.Data.Entity; ke global.asax. Terima kasih.
Dr. MAF

Berhasil. Cukup tambahkan kode di atas di Global.asax, itu saja, Tidak perlu mengimpor menggunakan System.Data.Entity;
Hemant Ramphul

52

Memberikan jawaban yang benar adalah salah satu cara untuk pergi, namun itu berlebihan ketika Anda dapat memperbaikinya dengan satu pengaturan konfigurasi.

Lebih baik menggunakannya di konstruktor dbcontext

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

Kesalahan API Web Asp.Net: Jenis 'ObjectContent`1' gagal untuk membuat serial tubuh tanggapan untuk jenis konten 'application / xml; charset = utf-8 '


kode Anda akan dihapus jika kami memperbarui model dari database.
Bimal Das

1
Anda dapat memisahkannya dengan mudah dengan menghapus file .tt dan memiliki konteks yang terputus-putus. Setiap kali Anda menghasilkan model, tambahkan kelas baru di tempat itu. @Brimal: Anda dapat mengikuti youtube.com/watch
Md. Alim Ul Karim

1
Untuk menghindari penimpaan, Anda dapat menonaktifkan pemuatan lambat, dari properti edmx. Itu berhasil untuk saya.
Francisco G

@FranciscoG berhasil tetapi hilang jika kita menghapus edmx dan membuat ulang.
Md. Alim Ul Karim

1
@BimalDas coba youtube.com/… ini . Ini tidak akan menghapus
Md. Alim Ul Karim

37

Tambahkan kode ini di global.asaxbawah pada Application_Start:

Perbarui dari .Ignoremenjadi .Serialize. Ini harus berhasil.

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

1
Bekerja dengan baik, terima kasih !, Bisakah Anda menjelaskan dengan lebih baik mengapa kami harus menghapus Xml Formatter untuk membuatnya berfungsi?
Jav_1

Tidak perlu menambahkan json serialiser (setidaknya dalam kasus saya) tetapi menghapus format Xml diperlukan. Saya kira xml serialiser tidak dapat membuat serial jenis anonim dan dengan menghapusnya hasilnya akan diserialkan sebagai json. Jika tebakan saya benar, seseorang akan bisa mendapatkan data dari pengontrol dengan menanyakan jenis 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();
    }
}

Wow, berhasil untukku. Tapi kenapa? Apa yang dilakukan properti ProxyCreationEnabled?
jacktric

bekerja dengan saya tapi apa kode ini? saya juga mencatat bahwa semua sub kelas diambil dengan null !!
Waleed A. Elgalil

10

Saya tidak suka kode ini:

foreach(var user in db.Users)

Sebagai alternatif, seseorang mungkin melakukan sesuatu seperti ini, yang berhasil untuk saya:

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

                         });

return listOfUsers.ToList();

Namun, saya akhirnya menggunakan solusi Lucas Roselli.

Pembaruan: Disederhanakan dengan mengembalikan objek anonim:

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

return listOfUsers.ToList();

10

Saya menyelesaikannya menggunakan kode ini ke file WebApiConfig.cs

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

Terima kasih banyak. tidak tahu apa pengaruhnya terhadap keamanan.
Arun Prasad ES

6

Ada juga skenario ini yang menghasilkan kesalahan yang sama:

Dalam kasus pengembalian menjadi List<dynamic>metode api web

Contoh:

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

Jadi, untuk skenario ini gunakan [DiketahuiTipeAttribute] di kelas kembalian (semuanya) seperti ini:

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

Ini berhasil untuk saya!


Bagaimana jika Anda menggunakan pivot linq dengan kolom dinamis?
codegrid

[DiketahuiTypeAttribute (typeof (TestClass))] memecahkan masalah saya
Guillaume Raymond

6

Menambahkan ini ke Application_Start()metode Global.asaxfile Anda akan menyelesaikan masalah

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

METODE 2: [Tidak disarankan]
Jika Anda bekerja dengan EntityFramework, Anda dapat menonaktifkan proxy di konstruktor kelas DbContext Anda. CATATAN: kode ini akan dihapus jika Anda memperbarui model

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

1
Terima kasih Suman. Saya mengalami masalah yang sama. Saya sedang menguji API web saya & terjebak dengan masalah ini. solusi Anda menyelesaikan masalah. Terima kasih banyak.
TarakPrajapati

4

Favorit pribadi saya: Cukup tambahkan kode di bawah ini ke App_Start/WebApiConfig.cs. Ini akan mengembalikan json sebagai ganti XML secara default dan juga mencegah kesalahan yang Anda alami. Tidak perlu mengedit Global.asaxuntuk menghapus XmlFormatterdll.

Jenis 'ObjectContent`1' gagal membuat serial tubuh respons untuk jenis konten 'application / xml; charset = utf-8

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

2

Gunakan AutoMapper ...

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

2

Gunakan namespace berikut:

using System.Web.OData;

Dari pada :

using System.Web.Http.OData;

Itu berhasil untuk saya


2

Tambahkan baris di bawah ini

this.Configuration.ProxyCreationEnabled = false;

Dua cara untuk digunakan ProxyCreationEnabledsebagai false.

  1. Tambahkan ke dalam DBContextConstructor

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

ATAU

  1. Tambahkan garis di dalam Getmetode

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

2

Solusi yang berhasil untuk saya:

  1. Gunakan [DataContract]untuk kelas dan [DataMember]atribut untuk setiap properti yang akan dibuat serial. Ini cukup untuk mendapatkan hasil Json (misalnya dari fiddler).

  2. Untuk mendapatkan serialisasi xml, tulis Global.asaxkode ini:

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

  3. Baca artikel ini, ini membantu saya untuk memahami serialisasi: https://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization

1

Untuk menambah jawaban jensendp:

Saya akan meneruskan entitas ke model yang dibuat pengguna dan menggunakan nilai dari entitas itu untuk mengatur nilai dalam model yang baru Anda buat. Sebagai contoh:

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

Kemudian ubah jenis pengembalian Anda menjadi: IEnumerable<UserInformation>


1
ada cara yang lebih elegan untuk menangani terjemahan untuk Anda sehingga Anda tidak perlu memelihara setiap properti .. AutoMapper dan ValueInjecter adalah 2 yang penting
Sonic Soul

1

Ini adalah kesalahanku

Saya pada dasarnya menambahkan satu baris yang mana mereka

  • entity.Configuration.ProxyCreationEnabled = false;

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

Ini keluaran saya:


1

Gunakan [Serializable] untuk kelas:

Contoh:

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

Itu berhasil untuk saya!


1
Diutarakan seperti ini, orang hampir dapat berpikir bahwa setiap orang yang menjawab posting ini sebelumnya adalah bodoh;)… Yah, saya benar-benar meragukan mereka, jadi itu mungkin berarti bahwa solusi ini sama sekali tidak berlaku ketika pertanyaan itu diajukan, pada tahun 2015 ... Saya sendiri tidak tahu banyak tentang sintaks itu, tetapi saya merasa itu relatif baru, atau mungkin ada beberapa kekurangan yang membuatnya tidak dapat digunakan dalam kasus penggunaan tertentu. Saya merasa bahwa solusi Anda dapat berguna bagi pembaca yang akan datang untuk menjawab pertanyaan ini, tetapi pasti akan membantu jika Anda menjelaskan batasannya.
jwatkins

1

Anda harus menentukan Serializer Formatter dalam WebApiConfig.cs yang tersedia di App_Start Folder seperti

Menambahkan config.Formatters.Remove (config.Formatters.XmlFormatter); // yang akan memberi Anda data dalam Format JSON

Menambahkan config.Formatters.Remove (config.Formatters.JsonFormatter); // yang akan memberi Anda data dalam Format XML


Anda berhak mendapatkan Medali.
TheKrogrammer

1

Letakkan saja baris berikut di global.asax:

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

Impor

using System.Data.Entity;

0

Kasus lain di mana saya menerima kesalahan ini adalah ketika kueri database saya mengembalikan nilai nol tetapi jenis model pengguna / tampilan saya ditetapkan sebagai non-nullable. Misalnya, mengubah bidang UserModel saya dari intmenjadi int?terselesaikan.


0

Ini juga terjadi jika Jenis-Tanggapan tidak bersifat publik! Saya mengembalikan kelas internal ketika saya menggunakan Visual Studio untuk menghasilkan saya tipe.

internal class --> public class

0

Meskipun semua jawaban di atas benar, Anda mungkin perlu memeriksa InnerException> ExceptionMessage .

Jika tertulis sesuatu seperti ini " Instance ObjectContext telah dibuang dan tidak dapat lagi digunakan untuk operasi yang memerlukan koneksi. ". Ini bisa menjadi masalah karena perilaku default EF.

Dengan menetapkan LazyLoadingEnabled = false di konstruktor DbContext Anda akan melakukan triknya.

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

Untuk bacaan yang lebih detail tentang perilaku EagerLoading dan LazyLoading dari EF lihat Artikel MSDN ini .


0

Dalam kasus saya, saya memiliki pesan kesalahan serupa:

Jenis 'ObjectContent`1' gagal membuat serial tubuh respons untuk jenis konten 'application / xml; charset = utf-8 '.

Tetapi ketika saya menggali lebih dalam, masalahnya adalah:

Ketik 'name.SomeSubRootType' dengan nama kontrak data 'SomeSubRootType: //schemas.datacontract.org/2004/07/WhatEverService' tidak diharapkan. Pertimbangkan untuk menggunakan DataContractResolver jika Anda menggunakan DataContractSerializer atau menambahkan tipe apa pun yang tidak dikenal secara statis ke daftar tipe yang dikenal - misalnya, dengan menggunakan atribut DiketahuiTipeAttribute atau dengan menambahkannya ke daftar tipe yang diketahui yang diteruskan ke serializer.

Cara saya menyelesaikannya dengan menambahkan KnownType.

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

Ini dipecahkan terinspirasi dari jawaban ini .

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


0

Visual Studio 2017 atau 2019 sama sekali tidak bijaksana dalam hal ini, karena Visual Studio sendiri membutuhkan output dalam format json , sedangkan format default Visual Studio adalah " XmlFormat" (config.Formatters.XmlFormatter) .

Visual Studio harus melakukan ini secara otomatis alih-alih memberi pengembang begitu banyak masalah.

Untuk memperbaiki masalah ini, buka file WebApiConfig.cs , dan tambahkan

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

setelah " config.MapHttpAttributeRoutes (); " di metode Register (HttpConfiguration config) . Ini akan memungkinkan proyek Anda menghasilkan keluaran json.


0

Dalam kasus saya, saya memecahkan pembuatan ulang database. Saya membuat beberapa perubahan dalam model dan meluncurkan Update-Database di Package Manager Console. Saya mendapat Error berikut:

"Pernyataan ALTER TABLE berkonflik dengan batasan FOREIGN KEY" FK_dbo.Activities_dbo.Projects_ProjectId ". Konflik terjadi dalam database" TrackEmAllContext-20190530144302 ", tabel" dbo.Projects ", kolom 'Id'."


0

Dalam kasus: Jika menambahkan kode ke WebApiConfig.cs atau Global.asax.cs tidak berhasil untuk Anda:

.ToList();

Tambahkan fungsi .ToList ().

Saya mencoba setiap solusi tetapi yang berikut berhasil untuk saya:

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

Saya harap, ini membantu.


0

dalam kasus saya, itu diperbaiki ketika saya menghapus kata kunci virtual sebelum properti navigasi saya, maksud saya tabel referensi. jadi saya berubah

public virtual MembershipType MembershipType { get; set; }

untuk:

public MembershipType MembershipType { get; set; }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.