Deserializzazione dei dati JSON su C # utilizzando JSON.NET


144

Sono relativamente nuovo a lavorare con i dati C # e JSON e sto cercando una guida. Sto usando C # 3.0, con .NET3.5SP1 e JSON.NET 3.5r6.

Ho una classe C # definita che devo popolare da una struttura JSON. Tuttavia, non tutte le strutture JSON per una voce recuperata dal servizio Web contengono tutti i possibili attributi definiti nella classe C #.

Ho fatto quello che sembra essere il modo sbagliato, difficile e ho semplicemente selezionato ogni valore uno per uno dal JObject e trasformando la stringa nella proprietà della classe desiderata.

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

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

Qual è il modo migliore per deserializzare una struttura JSON nella classe C # e gestire i possibili dati mancanti dall'origine JSON?

La mia classe è definita come:

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

  }

E un esempio del JSON da analizzare è:

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

Risposte:



76

Hai provato a utilizzare il metodo generico DeserializeObject?

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

Eventuali campi mancanti nei dati JSON devono essere semplicemente lasciati NULL.

AGGIORNARE:

Se la stringa JSON è un array, prova questo:

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

jarraydovrebbe quindi essere un List<MyAccount>.

UN ALTRO AGGIORNAMENTO:

L'eccezione che stai ottenendo non è coerente con una matrice di oggetti: penso che il serializzatore stia riscontrando problemi con la accountstatusmodifiedbyproprietà digitata dal dizionario .

Prova a escludere la accountstatusmodifiedby proprietà dalla serializzazione e verifica se ciò aiuta. In tal caso, potrebbe essere necessario rappresentare quella proprietà in modo diverso.

Documentazione: serializzazione e deserializzazione di JSON con Json.NET


Grazie. Tuttavia, viene visualizzato l'errore "Impossibile deserializzare l'array JSON nel tipo" System.String "." quando sta cercando di deserializzare (ad esempio) l'array givenname JSON nella stringa di classe GivenName. Gli attributi JSON che ho definito come stringa nella classe C # sono matrici a singolo elemento. Questo è il motivo per cui ho iniziato a individuare i valori uno ad uno mentre mi imbattevo in questo tipo di problema durante il processo di deserializzazione. Altra magia che sto trascurando?

Quindi ... DateTime AccountStatusExpiration(ad esempio) non è nullable come definito nel codice. Cosa ci vorrebbe per renderlo nullable? Passa semplicemente DateTimea DateTime??
Hamish Grubijan,

50

Risposta riprodotta da https://stackoverflow.com/a/10718128/776476

Puoi usare il dynamictipo C # per semplificare le cose. Questa tecnica semplifica anche il ricopertura in quanto non si basa su stringhe magiche.

jSON

La jsonstringa di seguito è una semplice risposta da una chiamata api http e definisce due proprietà: Ide Name.

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

C #

Utilizzare JsonConvert.DeserializeObject<dynamic>()per deserializzare questa stringa in un tipo dinamico, quindi accedere semplicemente alle sue proprietà nel solito modo.

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

Nota : il collegamento NuGet per l'assembly NewtonSoft è http://nuget.org/packages/newtonsoft.json . Non dimenticare di aggiungere: using Newtonsoft.Json;per accedere a quelle classi.


7
Adoro l'uso di <dinamico>. Tuttavia, ho dovuto farlo per farlo funzionare: risultato ["Id"]. Valore e risultato ["Nome"]. Valore
fredw

1
Sebbene la dinamica sia una buona alternativa, gli esempi precedenti non usano "stringhe magiche" ma usano invece generici fortemente tipizzati che vengono aggiornati durante le normali tecniche di refactoring VS (a meno che all'interno di una vista in MVC che non rientri nell'ambito di questa domanda).
gcoleman0828,

4
Hai ancora "stringhe magiche", ora sono semplicemente nascoste dall'uso della dinamica!
Ian Ringrose,

In effetti, il biofrattale ha indietro le "stringhe magiche", come sottolinea @ IanRingrose. Per DeserializeObject<dynamic>definizione, l' oggetto dinamico restituito non è "controllato dal tipo". Quindi le seguenti righe results.Ide results.Namenon possono essere verificate in fase di compilazione. Eventuali errori si verificheranno invece in fase di esecuzione. In contrasto con una chiamata fortemente tipizzata come DeserializeObject<List<MyAccount>>. I riferimenti a tali proprietà possono essere confermati in fase di compilazione. L'uso di dynamic, sebbene possa essere conveniente, introduce "stringhe magiche" come nomi di proprietà; una riduzione della robustezza IMHO.
ToolmakerSteve

8

Puoi usare:

JsonConvert.PopulateObject(json, obj);

qui: jsonè la stringa json, objè l'oggetto di destinazione. Vedi: esempio

Nota: PopulateObject()non cancellerà i dati della lista di obj, dopo Populate(), il obj'smembro della lista conterrà i suoi dati originali e i dati dalla stringa json


2
è PopulateObject - Populate non è nel modello a oggetti.
amato il

1
Questo ha funzionato PERFETTO per me! Avevo problemi in cui avevo una stringa JSON piuttosto complessa, quando provavo a lanciarla su oggetti C #, qualsiasi cosa contrassegnato come 'NotNull' mancava dall'oggetto, anche se all'inizio era presente nella stringa JSON. Molto strano. Ho usato questo metodo e ha funzionato PERFETTO!
jward01

3

Basandomi sulla risposta di bbant, questa è la mia soluzione completa per deserializzare JSON da un URL remoto.

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

L'utilizzo sarebbe come:

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

Dove FeedResultviene generata la classe utilizzando il generatore di classi JSON Xamasoft

Ecco uno screenshot delle impostazioni che ho usato, che consente nomi di proprietà strani che la versione Web non è in grado di rappresentare.

Xamasoft JSON Class Generator


1

Ho scoperto che avevo costruito l'oggetto in modo errato. Ho usato http://json2csharp.com/ per generarmi la mia classe di oggetti dal JSON. Una volta ottenuto l'Oject corretto sono stato in grado di lanciare senza problemi. Norbit, errore Noob. Ho pensato di aggiungerlo nel caso avessi lo stesso problema.


1

Puoi provare a controllare alcuni dei generatori di classe online per ulteriori informazioni. Tuttavia, credo che alcune delle risposte siano state utili. Ecco il mio approccio che può essere utile.

Il codice seguente è stato creato pensando a un metodo dinamico.

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

Questo codice consente sostanzialmente di accedere ai membri contenuti nella stringa Json. Solo un modo diverso senza il bisogno delle lezioni. query, trendE urlsono gli oggetti contenuti nella stringa JSON.

È inoltre possibile utilizzare questo sito Web . Non fidarti delle lezioni al 100%, ma hai l'idea.


0

Supponendo che i tuoi dati di esempio siano corretti, il tuo givenname e le altre voci racchiuse tra parentesi sono array in JS ... ti consigliamo di utilizzare Elenco per quei tipi di dati. ed elenco per dire accounttatusexpmaxdate ... Penso che il tuo esempio abbia le date erroneamente formattate, così incerti su cos'altro non è corretto nel tuo esempio.

Questo è un vecchio post, ma volevo prendere nota dei problemi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.