Deserializing data JSON ke C # menggunakan JSON.NET

144

Saya relatif baru bekerja dengan data C # dan JSON dan saya mencari panduan. Saya menggunakan C # 3.0, dengan .NET3.5SP1, dan JSON.NET 3.5r6.

Saya memiliki kelas C # yang saya butuhkan untuk mengisi dari struktur JSON. Namun, tidak setiap struktur JSON untuk entri yang diambil dari layanan web berisi semua atribut yang mungkin ditentukan dalam kelas C #.

Saya telah melakukan apa yang tampaknya menjadi cara yang salah, sulit dan hanya memilih setiap nilai satu per satu dari JObject dan mengubah string menjadi properti kelas yang diinginkan.

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

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

Apa cara terbaik untuk deserialize struktur JSON ke dalam kelas C # dan menangani kemungkinan data yang hilang dari sumber JSON?

Kelas saya didefinisikan sebagai:

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

  }

Dan contoh JSON untuk diurai adalah:

{
    "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
sumber

Jawaban:

76

Sudahkah Anda mencoba menggunakan metode DeserializeObject generik?

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

Setiap bidang yang hilang dalam data JSON harus dibiarkan NULL.

MEMPERBARUI:

Jika string JSON adalah array, coba ini:

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

jarrayharus menjadi List<MyAccount>.

PEMBARUAN LAIN:

Pengecualian yang Anda dapatkan tidak konsisten dengan array objek - Saya pikir serializer mengalami masalah dengan accountstatusmodifiedbyproperti yang diketikkan dengan Kamus Anda .

Cobalah mengecualikan accountstatusmodifiedby properti dari serialisasi dan lihat apakah itu membantu. Jika ya, Anda mungkin perlu mewakili properti itu secara berbeda.

Dokumentasi: Melakukan Serialisasi dan Deserialisasi JSON dengan Json.NET

Dave Swersky
sumber
Terima kasih. Namun saya mendapatkan kesalahan "Tidak dapat membatalkan deserialisasi array JSON menjadi tipe 'System.String'." ketika sedang mencoba untuk menghilangkan bakteri (sebagai contoh) array JSN yang diberikan ke dalam string GivenName kelas. Atribut JSON yang telah saya definisikan sebagai string di kelas C # hanya merupakan array elemen tunggal. Inilah sebabnya saya mulai memilih nilai-nilai satu per satu ketika saya mengalami masalah seperti ini selama proses deserialize. Keajaiban lain yang saya hadapi?
Jadi ... DateTime AccountStatusExpiration(misalnya) tidak dapat dibatalkan sebagaimana didefinisikan dalam kode. Apa yang diperlukan untuk membuatnya nullable? Cukup ubah DateTimemenjadi DateTime??
Hamish Grubijan
50

Jawaban direproduksi dari https://stackoverflow.com/a/10718128/776476

Anda dapat menggunakan tipe C # dynamicuntuk mempermudah. Teknik ini juga membuat re-factoring lebih sederhana karena tidak bergantung pada string-sihir.

Json

The jsonString di bawah ini adalah respon sederhana dari panggilan http api dan mendefinisikan dua sifat: Iddan Name.

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

C #

Gunakan JsonConvert.DeserializeObject<dynamic>()untuk menghilangkan tanda dari string ini menjadi tipe dinamis lalu cukup mengakses propertinya dengan cara yang biasa.

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

Catatan : Tautan NuGet untuk perakitan NewtonSoft adalah http://nuget.org/packages/newtonsoft.json . Jangan lupa menambahkan: using Newtonsoft.Json;untuk mengakses kelas-kelas itu.

biofractal
sumber
7
Senang menggunakan <dynamic>. Namun, saya harus melakukan ini agar berfungsi: result ["Id"]. Value dan result ["Name"]. Value
fredw
1
Meskipun dinamis adalah alternatif yang bagus, contoh-contoh di atas tidak menggunakan 'string ajaib' tetapi sebaliknya menggunakan generik yang sangat diketik yang diperbarui selama teknik refactoring VS normal (kecuali di dalam View in MVC yang berada di luar lingkup pertanyaan ini).
gcoleman0828
4
Anda masih memiliki 'string ajaib', mereka sekarang hanya disembunyikan oleh penggunaan dinamis!
Ian Ringrose
Memang, biofractal memilikinya mundur "string ajaib", seperti yang ditunjukkan @ IanRingrose. Objek dinamis yang dikembalikan oleh DeserializeObject<dynamic>adalah dengan definisi tidak "ketik-diperiksa". Jadi baris berikut results.Iddan results.Nametidak dapat diverifikasi pada waktu kompilasi. Setiap kesalahan akan terjadi pada saat run-time. Bandingkan ini dengan panggilan yang sangat diketik seperti DeserializeObject<List<MyAccount>>. Referensi ke properti tersebut dapat dikonfirmasi pada waktu kompilasi. Penggunaan dynamic, meskipun mungkin nyaman, memperkenalkan "string ajaib" sebagai nama properti; pengurangan IMHO ketahanan.
ToolmakerSteve
8

Kamu bisa memakai:

JsonConvert.PopulateObject(json, obj);

di sini: jsonadalah string json, objadalah objek target. Lihat: contoh

Catatan: PopulateObject()tidak akan menghapus data daftar obj, setelah itu Populate(), obj'sanggota daftar akan berisi data asli dan data dari string json

bbants
sumber
2
itu PopulateObject - Populate tidak ada dalam model objek.
mengamuk
1
Ini berhasil SEMPURNA untuk saya! Saya mengalami masalah di mana saya memiliki string JSON yang cukup kompleks, ketika saya mencoba untuk melemparkannya ke objek C #, apa pun yang ditandai sebagai 'NotNull' akan hilang dari objek, meskipun sudah ada di string JSON untuk memulai. Sangat aneh. Saya menggunakan metode ini dan itu berhasil SEMPURNA!
jward01
3

Membangun dari jawaban bbant, ini adalah solusi lengkap saya untuk deserializing JSON dari URL jarak jauh.

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

Penggunaan akan seperti:

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

Di mana FeedResultkelas yang dihasilkan menggunakan Xamasoft JSON Class Generator

Berikut adalah screenshot dari pengaturan yang saya gunakan, memungkinkan untuk nama properti aneh yang versi web tidak dapat menjelaskannya.

Xamasoft JSON Class Generator

Kyle Falconer
sumber
1

Saya menemukan saya saya telah membangun objek saya salah. Saya menggunakan http://json2csharp.com/ untuk menghasilkan kelas objek saya dari JSON. Setelah saya memiliki Oject yang benar, saya dapat melakukan tanpa masalah. Norbit, kesalahan Noob. Kupikir saya akan menambahkannya jika Anda memiliki masalah yang sama.

Adam
sumber
1

Anda dapat mencoba memeriksa beberapa generator kelas online untuk informasi lebih lanjut. Namun, saya yakin beberapa jawaban bermanfaat. Inilah pendekatan saya yang mungkin berguna.

Kode berikut dibuat dengan metode dinamis dalam pikiran.

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

Kode ini pada dasarnya memungkinkan Anda untuk mengakses anggota yang terkandung dalam string Json. Cara yang berbeda tanpa perlu kelas. query, trenddan urlbenda-benda yang terkandung dalam string Json.

Anda juga dapat menggunakan situs web ini . Jangan mempercayai kelas 100%, tetapi Anda mendapatkan idenya.

Edward Newgate
sumber
0

Dengan asumsi data sampel Anda benar, nama Anda, dan entri lain yang dibungkus dengan tanda kurung adalah array di JS ... Anda akan ingin menggunakan Daftar untuk tipe data tersebut. dan Daftar untuk mengatakan akunstatusexpmaxtanggal ... Saya pikir Anda contoh memiliki tanggal yang salah diformat, jadi tidak pasti apa yang salah dalam contoh Anda.

Ini adalah pos lama, tetapi ingin mencatat masalah tersebut.

Pelacak1
sumber