Nie udało się serializować odpowiedzi w internetowym interfejsie API za pomocą Json

109

Pracuję z ASP.NET MVC 5 Web Api. Chcę skonsultować się ze wszystkimi moimi użytkownikami.

Napisałem api/usersi otrzymuję to:

„Nie udało się serializować treści odpowiedzi dla typu„ ObjectContent ”1 dla typu zawartości„ application / json; charset = utf-8 ”.

W WebApiConfig dodałem już te linie:

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

Ale to nadal nie działa.

Moja funkcja zwracania danych jest następująca:

public IEnumerable<User> GetAll()
{
    using (Database db = new Database())
    {
        return db.Users.ToList();
    }
}
CampDev
źródło
Jak wygląda obiekt wartości, który próbujesz przekazać konsumentowi?
mckeejm
Dzięki wielkie! Po prostu fyi - myślę, że powinno to brzmieć: using (Database db = new Database ()) {List <UserModel> listOfUsers = new List <UserModel> (); foreach (użytkownik var w db.Users) {UserModel userModel = new UserModel (); userModel.FirstName = user.FirstName; userModel.LastName = user.LastName; listOfUsers.Add (userModel); } IEnumerable <UserModel> users = listOfUsers; powracających użytkowników; } .. ponieważ wszystkie wyniki zwracały te same wartości.
Jared Whittington,

Odpowiedzi:

76

Jeśli chodzi o zwracanie danych z powrotem do konsumenta z Web Api (lub jakiejkolwiek innej usługi sieciowej w tym zakresie), zdecydowanie odradzam przekazywanie podmiotów pochodzących z bazy danych. O wiele bardziej niezawodne i łatwiejsze w utrzymaniu jest używanie modeli, w których masz kontrolę nad wyglądem danych, a nie bazy danych. W ten sposób nie musisz zbytnio bawić się elementami formatującymi w WebApiConfig. Możesz po prostu utworzyć model użytkownika, który ma modele potomne jako właściwości i pozbyć się pętli odwołań w zwracanych obiektach. To sprawia, że ​​serializator jest znacznie szczęśliwszy.

Ponadto nie ma potrzeby usuwania elementów formatujących lub obsługiwanych typów multimediów, zazwyczaj jeśli w żądaniu określasz tylko nagłówek „Accepts”. Zabawa z tymi rzeczami może czasami sprawić, że sytuacja będzie bardziej zagmatwana.

Przykład:

public class UserModel {
    public string Name {get;set;}
    public string Age {get;set;}
    // Other properties here that do not reference another UserModel class.
}
jensendp
źródło
Kiedy odnosisz się do Modelek, chcesz powiedzieć, co robię? Zwróć IEnumerable użytkowników, którzy są modelem.
CampDev
5
Zwracasz Jednostkę. Jednostka odnosi się do obiektu w bazie danych, który można pobrać za pomocą unikalnego identyfikatora. Zwracasz wszystkie jednostki użytkownika ze swojej bazy danych. Sugeruję, aby utworzyć nową klasę o nazwie „UserModel” i dla każdej encji użytkownika uzyskanej z bazy danych utworzyć nową instancję klasy modelu danych wypełnioną niezbędnymi informacjami, które chcesz ujawnić. Zwraca IEnumerable obiektów UserModel w przeciwieństwie do jednostek User. Upewnij się, że właściwości modelu nie odwołują się do wystąpień klasy UserModel. To właśnie sprawia, że ​​wkraczasz w ten problem.
jensendp
3
ncampuzano jest poprawne, nie chcesz zwracać automatycznie generowanych jednostek. Gdybyś używał procedur składowanych do dostępu do bazy danych, separacja byłaby bardziej przejrzysta. Konieczne byłoby wygenerowanie obiektu wartości C # i zamapowanie wartości z IDataReader na obiekt wartości. Ponieważ używasz EF, generowane są klasy, ale są to specjalne klasy EF, które wiedzą więcej niż obiekty wartości. Powinieneś zwracać klientowi tylko „głupie” obiekty wartości.
mckeejm
1
@Donny Jeśli używasz DBContext lub repozytorium w kontrolerze, które zwraca jednostki z powrotem z bazy danych, możesz po prostu zmapować obiekty do modeli (na przykład DTO) w kontrolerze ... ale wolę mieć kontroler wywołuje usługę, która zwraca model / DTO. Sprawdź AutoMapper - świetne narzędzie do obsługi mapowania.
ben
1
@NH. Możesz bezwzględnie skorzystać z wyżej wymienionych shenanigans, ale wszystko ma swoje miejsce. „Jednostki” zapewniane przez dostęp do warstwy danych powinny zazwyczaj pozostać w warstwie danych. Wszystko, co chce korzystać z tych danych w warstwie biznesowej aplikacji, będzie zazwyczaj używać „jednostek” również w przekształconej formie (obiekty domeny). A następnie dane, które są zwracane i wprowadzane przez użytkownika, będą zwykle również innym formularzem (modele). Uzgodniono, że wykonywanie tego typu transformacji wszędzie może być żmudne, ale w tym miejscu narzędzia takie jak AutoMapper naprawdę się przydają.
jensendp
147

Jeśli pracujesz z EF, oprócz dodania poniższego kodu na Global.asax

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

Nie zapomnij zaimportować

using System.Data.Entity;

Następnie możesz zwrócić własne modele EF

Proste!

Lucas Roselli
źródło
Nawet jeśli może to pomóc w przypadku EF, rozwiązanie nie jest specyficzne dla EF i działa również z innymi typami modeli. Użycie nie wydaje się być konieczne w Global.asax. Czy był przeznaczony dla kontrolerów?
Matthieu
16
Mile widziane byłyby pewne wyjaśnienia dotyczące tego, co robi ten kod i jakie są tego konsekwencje.
Jacob
1
Dziękuję, że miałem podobny problem, ta odpowiedź pomogła mi rozwiązać problem.
RK_Aus
Mi to pasuje. Nie ma potrzeby dodawania za pomocą System.Data.Entity; do global.asax. Dziękuję Ci.
Dr. MAF
To działa. Proste dodanie kodu powyżej na Global.asax, to wszystko, Nie ma potrzeby importowania przy użyciu System.Data.Entity;
Hemant Ramphul
52

Podana prawidłowa odpowiedź jest jednym ze sposobów, jednak jest to przesada, gdy można to naprawić za pomocą jednego ustawienia konfiguracji.

Lepiej użyć go w konstruktorze dbcontext

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

Błąd interfejsu API sieci Web Asp.Net: dla typu „ObjectContent” 1 nie można serializować treści odpowiedzi dla typu zawartości „application / xml”; charset = utf-8 '

Md. Alim Ul Karim
źródło
Twój kod zostanie usunięty, jeśli zaktualizujemy model z bazy danych.
Bimal Das
1
Możesz go łatwo oddzielić, usuwając pliki .tt i mając rozłączny kontekst. Za każdym razem, gdy generujesz model, po prostu dodaj nową klasę w miejscu. @Brimal: Możesz śledzić to youtube.com/watch?v=yex0Z6qwe7A
Md. Alim Ul Karim
1
Aby uniknąć nadpisywania, możesz wyłączyć leniwe ładowanie we właściwościach edmx. U mnie to zadziałało.
Francisco G
@FranciscoG to działa, ale znika, jeśli usuniemy edmx i zregenerujemy.
dr Alim Ul Karim,
1
@BimalDas spróbuj tego youtube.com/… . To nie usunie
Md. Alim Ul Karim
37

Dodaj ten kod global.asaxponiżej w Application_Start:

Aktualizuj z .Ignoredo .Serialize. To musi działać.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
            GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Bimal Das
źródło
1
Działa świetnie, dzięki! Czy mógłbyś lepiej wyjaśnić, dlaczego musimy usunąć Xml Formatter, aby działał?
Jav_1
Nie ma potrzeby dodawania serializatora json (przynajmniej w moim przypadku), ale konieczne było usunięcie formatowania Xml. Wydaje mi się, że serializator xml nie może serializować anonimowych typów i po usunięciu go wynik jest serializowany jako json. Jeśli moje przypuszczenie jest poprawne, można byłoby uzyskać dane z kontrolera, prosząc o typ 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();
    }
}
Aykut
źródło
Wow, to zadziałało dla mnie. Ale dlaczego? Co robi właściwość ProxyCreationEnabled?
jacktric
pracował ze mną ale co to za kod? Zauważyłem również, że wszystkie podklasy pobrane z null !!
Waleed A. Elgalil,
10

Nie podoba mi się ten kod:

foreach(var user in db.Users)

Alternatywnie można zrobić coś takiego, co u mnie zadziałało:

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

                         });

return listOfUsers.ToList();

Skończyło się jednak na rozwiązaniu Lucasa Rosellego.

Aktualizacja: Uproszczone przez zwrócenie anonimowego obiektu:

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

return listOfUsers.ToList();
Kasper Halvas Jensen
źródło
10

Rozwiązałem to używając tego kodu do pliku WebApiConfig.cs

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; 
config.Formatters.Remove(config.Formatters.XmlFormatter);
aemre
źródło
wielkie dzięki. nie wiem jednak, jak to wpływa na bezpieczeństwo.
Arun Prasad ES
6

Istnieje również ten scenariusz, który generuje ten sam błąd:

W przypadku, gdy powrót jest List<dynamic>metodą api sieci web

Przykład:

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

Dlatego w tym scenariuszu użyj [KnownTypeAttribute] w klasie zwracanej (wszystkich) w następujący sposób:

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

To działa dla mnie!

Alex
źródło
Co jeśli używasz Linq Pivot z dynamicznymi kolumnami?
codegrid
[KnownTypeAttribute (typeof (TestClass))] rozwiązał mój problem
Guillaume Raymond
6

Dodanie tego do Application_Start()metody Global.asaxpliku powinno rozwiązać problem

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

METODA 2: [Niezalecane]
Jeśli pracujesz z EntityFramework, możesz wyłączyć serwer proxy w konstruktorze klasy DbContext. UWAGA: ten kod zostanie usunięty, jeśli zaktualizujesz model

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.ProxyCreationEnabled = false;
  }
}
Er Suman G.
źródło
1
Dzięki Suman. Miałem ten sam problem. Testowałem mój internetowy interfejs API i utknąłem z tym problemem. Twoje rozwiązanie rozwiązuje problem. wielkie dzięki.
TarakPrajapati
4

Mój osobisty ulubiony: po prostu dodaj poniższy kod do App_Start/WebApiConfig.cs. Spowoduje to domyślnie zwrócenie pliku json zamiast XML, a także zapobiegnie wystąpieniu błędu. Nie ma potrzeby edycji, Global.asaxaby usunąć XmlFormatteritp.

Nie udało się serializować treści odpowiedzi dla typu „ObjectContent” 1 dla typu zawartości „application / xml”; charset = utf-8

config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
Ogglas
źródło
2

Użyj AutoMapper ...

public IEnumerable<User> GetAll()
    {
        using (Database db = new Database())
        {
            var users = AutoMapper.Mapper.DynamicMap<List<User>>(db.Users);
            return users;
        }
    }
Proximo
źródło
2

Użyj następującej przestrzeni nazw:

using System.Web.OData;

Zamiast :

using System.Web.Http.OData;

U mnie to zadziałało

Harish K.
źródło
2

Dodaj poniższą linię

this.Configuration.ProxyCreationEnabled = false;

Dwa sposoby użycia ProxyCreationEnabledjako false.

  1. Dodaj go do DBContextConstructor

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

LUB

  1. Dodaj linię wewnątrz Getmetody

    public IEnumerable<Brand_Details> Get()
    {
        using (ProductEntities obj = new ProductEntities())
        {
            this.Configuration.ProxyCreationEnabled = false;
            return obj.Brand_Details.ToList();
        }
    }
Sudipta Saha
źródło
2

Rozwiązanie, które zadziałało dla mnie:

  1. Użyj [DataContract]dla klasy i [DataMember]atrybutów dla każdej właściwości do serializacji. To wystarczy, aby otrzymać wynik Json (np. Ze skrzypka).

  2. Aby uzyskać serializację XML, napisz w Global.asaxtym kodzie:

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

  3. Przeczytaj ten artykuł, pomógł mi zrozumieć serializację: https://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
Lapenkov Vladimir
źródło
1

Aby dodać do odpowiedzi Jensendpa:

Przekazałbym jednostkę do modelu utworzonego przez użytkownika i użyłbym wartości z tej jednostki, aby ustawić wartości w nowo utworzonym modelu. Na przykład:

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

Następnie zmień typ zwrotu na: IEnumerable<UserInformation>

Greg A.
źródło
1
są bardziej eleganckie sposoby obsługi tłumaczenia za Ciebie, dzięki czemu nie musisz utrzymywać każdej właściwości. AutoMapper i ValueInjecter to dwa godne uwagi
Sonic Soul
1

To jest mój błąd

Zasadniczo dodaję jedną linię, którą są

  • entity.Configuration.ProxyCreationEnabled = false;

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

Oto mój wynik:

GUL EDA AYDEMİR
źródło
1

Użyj [Serializable] dla klasy:

Przykład:

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

U mnie zadziałało!

Msxmania
źródło
1
Ujmując to w ten sposób, można by pomyśleć, że każdy, kto wcześniej odpowiadał na ten post, był głupi;)… Cóż, poważnie wątpię, że tak było, więc pewnie oznacza to, że to rozwiązanie po prostu nie miało zastosowania, gdy zadawano pytanie, jeszcze w 2015 roku… Sam nie wiem zbyt wiele o tej składni, ale mam wrażenie, że albo jest ona stosunkowo nowa, albo mogą mieć pewne wady, które sprawiają, że jest bezużyteczna w niektórych przypadkach użycia. Mam wrażenie, że Twoje rozwiązanie mogłoby być przydatne dla przyszłych czytelników, którzy dotrą do tego pytania, ale z pewnością pomogłoby, gdybyś wyjaśnił jego ograniczenia.
jwatkins
1

Będziesz musiał zdefiniować Serializer Formatter w WebApiConfig.cs dostępnym w folderze App_Start, takim jak

Dodawanie config.Formatters.Remove (config.Formatters.XmlFormatter); // który dostarczy Ci dane w formacie JSON

Dodanie config.Formatters.Remove (config.Formatters.JsonFormatter); // który dostarczy Ci dane w formacie XML

Yogesh Dangre
źródło
Zasługujesz na medal.
TheKrogrammer
1

Po prostu umieść następujące wiersze w global.asax:

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

Import

using System.Data.Entity;
Sayyam abbasi
źródło
0

Innym przypadkiem, w którym otrzymałem ten błąd, było to, że moje zapytanie do bazy danych zwróciło wartość null, ale mój typ modelu użytkownika / widoku został ustawiony jako niepodlegający wartości null. Na przykład zmiana mojego pola UserModel z intna int?rozwiązane.

Marshall
źródło
0

Dzieje się tak również, gdy typ odpowiedzi nie jest publiczny! Zwróciłem klasę wewnętrzną, ponieważ użyłem programu Visual Studio do wygenerowania typu.

internal class --> public class
Vanice
źródło
0

Chociaż wszystkie powyższe odpowiedzi są poprawne, warto sprawdzić InnerException> ExceptionMessage .

Jeśli mówi coś takiego: „Instancja ObjectContext została usunięta i nie może być już używana do operacji wymagających połączenia. ”. Może to być problem z powodu domyślnego zachowania EF.

Przypisanie LazyLoadingEnabled = false w konstruktorze DbContext załatwi sprawę.

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

Aby uzyskać bardziej szczegółowe informacje na temat zachowania EF w trybie EagerLoading i LazyLoading, zapoznaj się z tym artykułem w witrynie MSDN .

Bhramar
źródło
0

W moim przypadku miałem podobny komunikat o błędzie:

Nie udało się serializować treści odpowiedzi dla typu „ObjectContent” 1 dla typu zawartości „application / xml”; charset = utf-8 '.

Ale kiedy głębiej się w to zagłębiłem, problem był następujący:

Wpisz „name.SomeSubRootType” z nazwą kontraktu danych „SomeSubRootType: //schemas.datacontract.org/2004/07/WhatEverService” nie jest oczekiwany. Rozważ użycie DataContractResolver, jeśli używasz DataContractSerializer lub dodaj dowolne typy nieznane statycznie do listy znanych typów - na przykład przy użyciu atrybutu KnownTypeAttribute lub dodając je do listy znanych typów przekazanych do serializatora.

Sposób, w jaki rozwiązałem, dodając KnownType.

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

Zostało to rozwiązane zainspirowane tą odpowiedzią .

Źródła: https://msdn.microsoft.com/en-us/library/ms730167(v=vs.100).aspx

maytham-ɯɐɥʇʎɐɯ
źródło
0

Program Visual Studio 2017 lub 2019 jest w tej kwestii całkowicie bezmyślny, ponieważ sam program Visual Studio wymaga, aby dane wyjściowe były w formacie json , podczas gdy domyślnym formatem programu Visual Studio jest „ XmlFormat” (config.Formatters.XmlFormatter) .

Visual Studio powinno robić to automatycznie, zamiast sprawiać programistom tyle problemów.

Aby rozwiązać ten problem, przejdź do pliku WebApiConfig.cs i dodaj

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

po „ config.MapHttpAttributeRoutes (); ” w metodzie Register (HttpConfiguration config) . Umożliwiłoby to projektowi wygenerowanie danych wyjściowych json.

William Hou
źródło
0

W moim przypadku rozwiązałem odtworzenie bazy danych. Dokonałem pewnych zmian w modelu i uruchomiłem Update-Database w konsoli Menedżera pakietów. Otrzymałem następujący błąd:

„Instrukcja ALTER TABLE spowodowała konflikt z ograniczeniem FOREIGN KEY„ FK_dbo.Activities_dbo.Projects_ProjectId ”. Konflikt wystąpił w bazie danych„ TrackEmAllContext-20190530144302 ”, tabela„ dbo.Projects ”, kolumna„ Id ”.”

Manuel Sansone
źródło
0

W przypadku: Jeśli dodanie kodu do WebApiConfig.cs lub Global.asax.cs nie działa dla Ciebie:

.ToList();

Dodaj funkcję .ToList ().

Wypróbowałem każde rozwiązanie, ale działały dla mnie następujące:

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

Mam nadzieję, że to pomoże.

Katalizator
źródło
0

w moim przypadku zostało to naprawione, gdy usunąłem słowo kluczowe virtual przed właściwościami nawigacji, mam na myśli tabele referencyjne. więc się zmieniłem

public virtual MembershipType MembershipType { get; set; }

do:

public MembershipType MembershipType { get; set; }
Hadi Rezaee
źródło