Deserializacja danych JSON do C # przy użyciu JSON.NET


144

Jestem stosunkowo nowy w pracy z danymi C # i JSON i szukam wskazówek. Używam C # 3.0 z .NET3.5SP1 i JSON.NET 3.5r6.

Mam zdefiniowaną klasę C #, którą muszę wypełnić ze struktury JSON. Jednak nie każda struktura JSON dla wpisu, który jest pobierany z usługi sieci Web, zawiera wszystkie możliwe atrybuty zdefiniowane w klasie C #.

Robiłem coś, co wydaje się być niewłaściwe, trudne i po prostu wybierałem każdą wartość po kolei z JObject i przekształcałem ciąg znaków w żądaną właściwość klasy.

JsonSerializer serializer = new JsonSerializer();
var o = (JObject)serializer.Deserialize(myjsondata);

MyAccount.EmployeeID = (string)o["employeeid"][0];

Jaki jest najlepszy sposób deserializacji struktury JSON do klasy C # i obsługi ewentualnych brakujących danych ze źródła JSON?

Moja klasa jest zdefiniowana jako:

  public class MyAccount
  {

    [JsonProperty(PropertyName = "username")]
    public string UserID { get; set; }

    [JsonProperty(PropertyName = "givenname")]
    public string GivenName { get; set; }

    [JsonProperty(PropertyName = "sn")]
    public string Surname { get; set; }

    [JsonProperty(PropertyName = "passwordexpired")]
    public DateTime PasswordExpire { get; set; }

    [JsonProperty(PropertyName = "primaryaffiliation")]
    public string PrimaryAffiliation { get; set; }

    [JsonProperty(PropertyName = "affiliation")]
    public string[] Affiliation { get; set; }

    [JsonProperty(PropertyName = "affiliationstatus")]
    public string AffiliationStatus { get; set; }

    [JsonProperty(PropertyName = "affiliationmodifytimestamp")]
    public DateTime AffiliationLastModified { get; set; }

    [JsonProperty(PropertyName = "employeeid")]
    public string EmployeeID { get; set; }

    [JsonProperty(PropertyName = "accountstatus")]
    public string AccountStatus { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpiration")]
    public DateTime AccountStatusExpiration { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpmaxdate")]
    public DateTime AccountStatusExpirationMaxDate { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifytimestamp")]
    public DateTime AccountStatusModified { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpnotice")]
    public string AccountStatusExpNotice { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifiedby")]
    public Dictionary<DateTime, string> AccountStatusModifiedBy { get; set; }

    [JsonProperty(PropertyName = "entrycreatedate")]
    public DateTime EntryCreatedate { get; set; }

    [JsonProperty(PropertyName = "entrydeactivationdate")]
    public DateTime EntryDeactivationDate { get; set; }

  }

Przykładem JSON do przeanalizowania jest:

{
    "givenname": [
        "Robert"
    ],
    "passwordexpired": "20091031041550Z",
    "accountstatus": [
        "active"
    ],
    "accountstatusexpiration": [
        "20100612000000Z"
    ],
    "accountstatusexpmaxdate": [
        "20110410000000Z"
    ],
    "accountstatusmodifiedby": {
        "20100214173242Z": "tdecker",
        "20100304003242Z": "jsmith",
        "20100324103242Z": "jsmith",
        "20100325000005Z": "rjones",
        "20100326210634Z": "jsmith",
        "20100326211130Z": "jsmith"
    },
    "accountstatusmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliation": [
        "Employee",
        "Contractor",
        "Staff"
    ],
    "affiliationmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliationstatus": [
        "detached"
    ],
    "entrycreatedate": [
        "20000922072747Z"
    ],
    "username": [
        "rjohnson"
    ],
    "primaryaffiliation": [
        "Staff"
    ],
    "employeeid": [
        "999777666"
    ],
    "sn": [
        "Johnson"
    ]
}

Odpowiedzi:



76

Czy próbowałeś użyć ogólnej metody DeserializeObject?

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

Wszelkie brakujące pola w danych JSON należy po prostu pozostawić NULL.

AKTUALIZACJA:

Jeśli ciąg JSON jest tablicą, spróbuj tego:

var jarray = JsonConvert.DeserializeObject<List<MyAccount>>(myjsondata);

jarraypowinien być List<MyAccount>.

KOLEJNA AKTUALIZACJA:

Wyjątek, który otrzymujesz, nie jest spójny z tablicą obiektów - myślę, że serializator ma problemy z twoją accountstatusmodifiedbywłaściwością typu Dictionary .

Spróbuj wykluczyć accountstatusmodifiedby właściwość z serializacji i sprawdź, czy to pomaga. Jeśli tak, może być konieczne inne przedstawienie tej właściwości.

Dokumentacja: serializacja i deserializacja JSON z Json.NET


Dzięki. Jednak pojawia się błąd „Nie można deserializować tablicy JSON do typu„ System.String ””. kiedy próbuje deserializować (na przykład) tablicę JSON nadana nazwa do ciągu klasy GivenName. Atrybuty JSON, które zdefiniowałem jako ciągi w klasie C #, są tylko tablicami pojedynczego elementu. Dlatego zacząłem wybierać wartości jeden po drugim, gdy natknąłem się na tego rodzaju problem podczas procesu deserializacji. Inna magia, którą przeoczam?

Więc ... DateTime AccountStatusExpiration(na przykład) nie dopuszcza wartości null zgodnie z definicją w kodzie. Co trzeba by zrobić, żeby to unieważnić? Po prostu zmienić DateTimena DateTime??
Hamish Grubijan

50

Odpowiedź została odtworzona z https://stackoverflow.com/a/10718128/776476

dynamicAby ułatwić sobie pracę, możesz użyć typu C # . Technika ta sprawia również, że ponowne fakturowanie jest prostsze, ponieważ nie polega na magicznych łańcuchach.

Json

Poniższy jsonciąg jest prostą odpowiedzią na wywołanie http api i definiuje dwie właściwości: Idi Name.

{"Id": 1, "Name": "biofractal"}

DO#

Służy JsonConvert.DeserializeObject<dynamic>()do deserializacji tego ciągu do typu dynamicznego, a następnie po prostu uzyskuj dostęp do jego właściwości w zwykły sposób.

var results = JsonConvert.DeserializeObject<dynamic>(json);
var id = results.Id;
var name= results.Name;

Uwaga : łącze NuGet dla zestawu NewtonSoft to http://nuget.org/packages/newtonsoft.json . Nie zapomnij dodać: using Newtonsoft.Json;aby uzyskać dostęp do tych klas.


7
Uwielbiam używanie <dynamic>. Jednak musiałem to zrobić, aby działał: wynik ["Id"]. Wartość i wynik ["Nazwa"]. Wartość
fredw

1
Chociaż dynamika jest fajną alternatywą, powyższe przykłady nie używają `` magicznych ciągów '', ale zamiast tego używają silnie wpisanych typów ogólnych, które są aktualizowane podczas normalnych technik refaktoryzacji VS (chyba że wewnątrz widoku w MVC, który jest poza zakresem tego pytania).
gcoleman0828

4
Wciąż masz „magiczne struny”, teraz są one po prostu ukryte dzięki zastosowaniu dynamiki!
Ian Ringrose

Rzeczywiście, biofraktal ma odwrotny charakter „magicznych strun”, jak wskazuje @ IanRingrose. Obiekt dynamiczny zwrócony przez DeserializeObject<dynamic>z definicji nie jest sprawdzany pod kątem typu. Więc następujące wiersze results.Idi results.Namenie można ich zweryfikować w czasie kompilacji. Zamiast tego wszelkie błędy wystąpią w czasie wykonywania. Porównaj to z wywołaniem o silnym typie, takim jak DeserializeObject<List<MyAccount>>. Odniesienia do tych właściwości można potwierdzić w czasie kompilacji. Użycie dynamic, choć może być wygodne, wprowadza „magiczne ciągi” jako nazwy właściwości; zmniejszenie odporności IMHO.
ToolmakerSteve

8

Możesz użyć:

JsonConvert.PopulateObject(json, obj);

tutaj: jsonto ciąg json, objto obiekt docelowy. Zobacz: przykład

Uwaga: PopulateObject()nie usunie dane użytkownika obj list, po Populate(), obj'sczłonek wola lista zawiera oryginalne dane i dane z json ciąg


2
to jest PopulateObject - Populate nie znajduje się w modelu obiektowym.
amok

1
To działało dla mnie IDEALNIE! Miałem problemy, gdy miałem dość złożony ciąg JSON, kiedy próbowałem rzutować go na obiekty C #, w obiekcie brakowałoby wszystkiego oznaczonego jako „NotNull”, mimo że był obecny w ciągu JSON na początku. Bardzo dziwny. Użyłem tej metody i zadziałała IDEALNIE!
jward01

3

Opierając się na odpowiedzi bbant, jest to moje kompletne rozwiązanie do deserializacji JSON ze zdalnego adresu URL.

using Newtonsoft.Json;
using System.Net.Http;

namespace Base
{
    public class ApiConsumer<T>
    {
        public T data;
        private string url;

        public CalendarApiConsumer(string url)
        {
            this.url = url;
            this.data = getItems();
        }

        private T getItems()
        {
            T result = default(T);
            HttpClient client = new HttpClient();

            // This allows for debugging possible JSON issues
            var settings = new JsonSerializerSettings
            {
                Error = (sender, args) =>
                {
                    if (System.Diagnostics.Debugger.IsAttached)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                }
            };

            using (HttpResponseMessage response = client.GetAsync(this.url).Result)
            {
                if (response.IsSuccessStatusCode)
                {
                    result = JsonConvert.DeserializeObject<T>(response.Content.ReadAsStringAsync().Result, settings);
                }
            }
            return result;
        }
    }
}

Sposób użycia będzie następujący:

ApiConsumer<FeedResult> feed = new ApiConsumer<FeedResult>("http://example.info/feeds/feeds.aspx?alt=json-in-script");

Gdzie FeedResultjest klasa wygenerowana przy użyciu generatora klas JSON firmy Xamasoft

Oto zrzut ekranu ustawień, których użyłem, pozwalających na dziwne nazwy właściwości, których wersja internetowa nie mogła uwzględnić.

Generator klas JSON firmy Xamasoft


1

Okazało się, że mój obiekt został zbudowany nieprawidłowo. Użyłem http://json2csharp.com/ do wygenerowania mojej klasy obiektu z JSON. Gdy miałem właściwy obiekt, mogłem rzucać bez problemu. Norbit, błąd Nooba. Pomyślałem, że dodam to na wypadek, gdybyś miał ten sam problem.


1

Możesz spróbować sprawdzić niektóre generatory klas online, aby uzyskać dalsze informacje. Uważam jednak, że niektóre odpowiedzi były przydatne. Oto moje podejście, które może być przydatne.

Poniższy kod został stworzony z myślą o metodzie dynamicznej.

dynObj = (JArray) JsonConvert.DeserializeObject(nvm);

foreach(JObject item in dynObj) {
 foreach(JObject trend in item["trends"]) {
  Console.WriteLine("{0}-{1}-{2}", trend["query"], trend["name"], trend["url"]);
 }
}

Ten kod zasadniczo umożliwia dostęp do elementów członkowskich zawartych w ciągu Json. Po prostu inny sposób bez konieczności zajęć.query, trendi urlsą obiektami zawartymi w ciągu Json.

Możesz także skorzystać z tej strony . Nie ufaj klasom w 100%, ale masz pomysł.


0

Zakładając, że Twoje przykładowe dane są poprawne, Twoje imię i nazwisko oraz inne wpisy w nawiasach to tablice w JS ... będziesz chciał użyć listy dla tych typów danych. i List for, powiedzmy accountstatusexpmaxdate ... Myślę, że przykład ma daty niepoprawnie sformatowane, tak niepewne, co jeszcze jest nieprawidłowe w twoim przykładzie.

To jest stary post, ale chciałem zwrócić uwagę na problemy.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.