Deserialisieren von JSON-Daten in C # mithilfe von JSON.NET

144

Ich bin relativ neu in der Arbeit mit C # - und JSON-Daten und suche Rat. Ich verwende C # 3.0 mit .NET3.5SP1 und JSON.NET 3.5r6.

Ich habe eine definierte C # -Klasse, die ich aus einer JSON-Struktur füllen muss. Nicht jede JSON-Struktur für einen Eintrag, der vom Webdienst abgerufen wird, enthält jedoch alle möglichen Attribute, die innerhalb der C # -Klasse definiert sind.

Ich habe den scheinbar falschen, harten Weg gemacht und einfach jeden Wert einzeln aus dem JObject herausgesucht und den String in die gewünschte Klasseneigenschaft umgewandelt.

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

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

Was ist der beste Weg, um eine JSON-Struktur in die C # -Klasse zu deserialisieren und mögliche fehlende Daten aus der JSON-Quelle zu verarbeiten?

Meine Klasse ist definiert als:

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

  }

Ein Beispiel für das zu analysierende JSON ist:

{
    "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"
    ]
}
Govind Malviya
quelle

Antworten:

277

Verwenden

var rootObject =  JsonConvert.DeserializeObject<RootObject>(string json);

Erstellen Sie Ihre Klassen in JSON 2 C #


Json.NET-Dokumentation: Serialisieren und Deserialisieren von JSON mit Json.NET

w.donk
quelle
32
Visual Studio verfügt außerdem über die Option JSON als Klassen einfügen im Menü Bearbeiten-Einfügen Spezial.
Jonathan Allen
76

Haben Sie versucht, die generische DeserializeObject-Methode zu verwenden?

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

Fehlende Felder in den JSON-Daten sollten einfach NULL bleiben.

AKTUALISIEREN:

Wenn die JSON-Zeichenfolge ein Array ist, versuchen Sie Folgendes:

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

jarraysollte dann ein sein List<MyAccount>.

EIN ANDERES UPDATE:

Die Ausnahme, die Sie erhalten, stimmt nicht mit einer Reihe von Objekten überein. Ich denke, der Serializer hat Probleme mit Ihrer accountstatusmodifiedbyEigenschaft vom Typ Dictionary .

Versuchen Sie, die accountstatusmodifiedby Eigenschaft von der Serialisierung auszuschließen, und prüfen Sie, ob dies hilfreich ist. In diesem Fall müssen Sie diese Eigenschaft möglicherweise anders darstellen.

Dokumentation: Serialisieren und Deserialisieren von JSON mit Json.NET

Dave Swersky
quelle
Vielen Dank. Es wird jedoch die Fehlermeldung "JSON-Array kann nicht in Typ 'System.String' deserialisiert werden" angezeigt. Wenn versucht wird, das JSON-Givenname-Array (zum Beispiel) in die Klasse GivenName-Zeichenfolge zu deserialisieren. Die JSON-Attribute, die ich in der C # -Klasse als Zeichenfolge definiert habe, sind immer nur Einzelelement-Arrays. Aus diesem Grund habe ich angefangen, die Werte einzeln auszuwählen, als ich während des Deserialisierungsprozesses auf diese Art von Problem gestoßen bin. Andere Magie, die ich übersehen habe?
Also ... DateTime AccountStatusExpiration(zum Beispiel) ist nicht nullbar, wie im Code definiert. Was würde es brauchen, um es nullbar zu machen? Einfach wechseln DateTimezu DateTime??
Hamish Grubijan
50

Antwort reproduziert von https://stackoverflow.com/a/10718128/776476

Sie können den C # dynamic-Typ verwenden, um die Arbeit zu vereinfachen. Diese Technik vereinfacht auch das Re-Factoring, da es nicht auf magischen Strings beruht.

Json

Die jsonZeichenfolge ist eine einfache Antwort von einem HTTP - API - Aufruf und definiert zwei Eigenschaften: Idund Name.

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

C #

Verwenden Sie JsonConvert.DeserializeObject<dynamic>()diese Option, um diese Zeichenfolge in einen dynamischen Typ zu deserialisieren und dann einfach auf die übliche Weise auf ihre Eigenschaften zuzugreifen.

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

Hinweis : Der NuGet-Link für die NewtonSoft-Assembly lautet http://nuget.org/packages/newtonsoft.json . Vergessen Sie nicht hinzuzufügen: using Newtonsoft.Json;um auf diese Klassen zuzugreifen.

Biofractal
quelle
7
Ich liebe die Verwendung von <dynamic>. Ich musste dies jedoch tun, um es zum
Laufen
1
Obwohl Dynamik eine gute Alternative ist, verwenden die obigen Beispiele keine "magischen Zeichenfolgen", sondern stark typisierte Generika, die während normaler VS-Refactoring-Techniken aktualisiert werden (es sei denn, innerhalb einer Ansicht in MVC, die außerhalb des Bereichs dieser Frage liegt).
gcoleman0828
4
Sie haben immer noch "magische Saiten", sie werden jetzt nur noch durch die Verwendung von Dynamik verborgen!
Ian Ringrose
In der Tat hat Biofractal es rückwärts wieder "magische Saiten", wie @ IanRingrose betont. Das von zurückgegebene dynamische Objekt DeserializeObject<dynamic>ist per Definition nicht "typgeprüft". Also die folgenden Zeilen results.Idund results.Namekönnen zur Kompilierungszeit nicht verifiziert werden. Alle Fehler treten stattdessen zur Laufzeit auf. Vergleichen Sie dies mit einem stark typisierten Anruf wie DeserializeObject<List<MyAccount>>. Verweise auf diese Eigenschaften können zur Kompilierungszeit bestätigt werden. Die Verwendung von dynamic, obwohl es zweckmäßig sein mag, führt "magische Zeichenfolgen" als Eigenschaftsnamen ein; eine Verringerung der Robustheit IMHO.
ToolmakerSteve
8

Sie können verwenden:

JsonConvert.PopulateObject(json, obj);

hier: jsonist der json string, objist das zielobjekt. Siehe: Beispiel

Hinweis: PopulateObject()nicht löschen obj Liste Daten nach Populate(), obj'sListenmitglied Willen enthält seine Originaldaten und Daten von JSON - String

bbants
quelle
2
es ist PopulateObject - Populate befindet sich nicht im Objektmodell.
Amok
1
Das hat bei mir PERFEKT funktioniert! Ich hatte Probleme, bei denen ich eine ziemlich komplexe JSON-Zeichenfolge hatte. Als ich versuchte, sie in C # -Objekte umzuwandeln, fehlte im Objekt alles, was als "NotNull" gekennzeichnet war, obwohl es zunächst in der JSON-Zeichenfolge vorhanden war. Sehr eigenartig. Ich habe diese Methode verwendet und es hat perfekt funktioniert!
jward01
3

Aufbauend auf der Antwort von bbant ist dies meine Komplettlösung zum Deserialisieren von JSON von einer Remote-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;
        }
    }
}

Verwendung wäre wie:

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

Wo FeedResultwird die Klasse mit dem Xamasoft JSON- Klassengenerator generiert ?

Hier ist ein Screenshot der Einstellungen, die ich verwendet habe, um seltsame Eigenschaftsnamen zu berücksichtigen, die in der Webversion nicht berücksichtigt werden konnten.

Xamasoft JSON-Klassengenerator

Kyle Falconer
quelle
1

Ich habe festgestellt, dass ich mein Objekt falsch erstellt habe. Ich habe http://json2csharp.com/ verwendet , um meine Objektklasse aus dem JSON zu generieren. Sobald ich das richtige Objekt hatte, konnte ich ohne Probleme wirken. Norbit, Noob Fehler. Ich dachte, ich würde es hinzufügen, falls Sie das gleiche Problem haben.

Adam
quelle
1

Sie können versuchen, einige der Klassengeneratoren online auf weitere Informationen zu überprüfen. Ich glaube jedoch, dass einige der Antworten nützlich waren. Hier ist mein Ansatz, der nützlich sein kann.

Der folgende Code wurde mit Blick auf eine dynamische Methode erstellt.

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"]);
 }
}

Mit diesem Code können Sie grundsätzlich auf Mitglieder zugreifen, die in der Json-Zeichenfolge enthalten sind. Nur ein anderer Weg ohne die Notwendigkeit der Klassen. query, trendUnd urlsind die Objekte in dem JSON - String enthalten ist .

Sie können diese Website auch nutzen . Vertraue den Klassen nicht zu 100%, aber du kommst auf die Idee.

Edward Newgate
quelle
0

Angenommen, Ihre Beispieldaten sind korrekt, Ihr Givenname und andere in Klammern gesetzte Einträge sind Arrays in JS ... Sie möchten List für diese Datentypen verwenden. und Liste zum Beispiel accountstatusexpmaxdate ... Ich denke, in Ihrem Beispiel sind die Daten jedoch falsch formatiert, so unsicher, was in Ihrem Beispiel sonst noch falsch ist.

Dies ist ein alter Beitrag, wollte aber die Probleme zur Kenntnis nehmen.

Tracker1
quelle