Cara terbaik untuk menangani situasi ini adalah dengan menggunakan kebiasaan JsonConverter
.
Sebelum kita masuk ke konverter, kita perlu menentukan kelas untuk deserialisasi datanya. Untuk Categories
properti yang dapat bervariasi antara satu item dan larik, tentukan sebagai a List<string>
dan tandai dengan [JsonConverter]
atribut sehingga JSON.Net akan tahu untuk menggunakan konverter khusus untuk properti itu. Saya juga akan merekomendasikan penggunaan [JsonProperty]
atribut sehingga properti anggota dapat diberi nama yang bermakna terlepas dari apa yang didefinisikan di JSON.
class Item
{
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("timestamp")]
public int Timestamp { get; set; }
[JsonProperty("event")]
public string Event { get; set; }
[JsonProperty("category")]
[JsonConverter(typeof(SingleOrArrayConverter<string>))]
public List<string> Categories { get; set; }
}
Berikut adalah bagaimana saya akan menerapkan konverter. Perhatikan saya telah membuat konverter generik sehingga dapat digunakan dengan string atau jenis objek lain sesuai kebutuhan.
class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Berikut adalah program singkat yang mendemonstrasikan konverter beraksi dengan data sampel Anda:
class Program
{
static void Main(string[] args)
{
string json = @"
[
{
""email"": ""[email protected]"",
""timestamp"": 1337966815,
""category"": [
""newuser"",
""transactional""
],
""event"": ""open""
},
{
""email"": ""[email protected]"",
""timestamp"": 1337966815,
""category"": ""olduser"",
""event"": ""open""
}
]";
List<Item> list = JsonConvert.DeserializeObject<List<Item>>(json);
foreach (Item obj in list)
{
Console.WriteLine("email: " + obj.Email);
Console.WriteLine("timestamp: " + obj.Timestamp);
Console.WriteLine("event: " + obj.Event);
Console.WriteLine("categories: " + string.Join(", ", obj.Categories));
Console.WriteLine();
}
}
}
Dan terakhir, inilah keluaran dari di atas:
email: [email protected]
timestamp: 1337966815
event: open
categories: newuser, transactional
email: [email protected]
timestamp: 1337966815
event: open
categories: olduser
Biola: https://dotnetfiddle.net/lERrmu
EDIT
Jika Anda perlu menggunakan cara lain, yaitu membuat serial, dengan tetap mempertahankan format yang sama, Anda dapat menerapkan WriteJson()
metode konverter seperti yang ditunjukkan di bawah ini. (Pastikan untuk menghapus CanWrite
penggantian atau mengubahnya untuk mengembalikan true
, atau tidak WriteJson()
akan pernah dipanggil.)
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
List<T> list = (List<T>)value;
if (list.Count == 1)
{
value = list[0];
}
serializer.Serialize(writer, value);
}
Biola: https://dotnetfiddle.net/XG3eRy
DeserializeObject
panggilan jika Anda menggunakan[JsonConverter]
atribut pada properti list di kelas Anda, seperti yang ditunjukkan pada jawaban di atas. Jika Anda tidak menggunakan atribut tersebut, ya, Anda harus meneruskan konverter keDeserializeObject
.List<T>
dalam konverterT[]
dan ubah.Count
ke.Length
. dotnetfiddle.net/vnCNgZSaya mengerjakan ini selama berabad-abad, dan terima kasih kepada Brian atas jawabannya. Yang saya tambahkan hanyalah jawaban vb.net !:
lalu di kelasmu:
Semoga ini menghemat waktu Anda
sumber
Sebagai variasi kecil pada jawaban yang bagus oleh Brian Rogers , di sini adalah dua versi tweak dari
SingleOrArrayConverter<T>
.Pertama, berikut adalah versi yang berfungsi untuk semua
List<T>
untuk setiap jenisT
yang bukan merupakan koleksi itu sendiri:Ini dapat digunakan sebagai berikut:
Catatan:
Konverter menghindari kebutuhan untuk memuat terlebih dahulu seluruh nilai JSON ke dalam memori sebagai
JToken
hierarki.Konverter tidak berlaku untuk daftar yang itemnya juga diserialkan sebagai koleksi, mis
List<string []>
Boolean
canWrite
Argumen diteruskan ke kontrol konstruktor apakah akan menyusun ulang daftar elemen tunggal sebagai nilai JSON atau sebagai array JSON.Konverter
ReadJson()
menggunakanexistingValue
if pra-alokasi untuk mendukung pengisian anggota daftar get-only.Kedua, berikut adalah versi yang berfungsi dengan koleksi umum lainnya seperti
ObservableCollection<T>
:Kemudian, jika model Anda menggunakan, katakanlah, an
ObservableCollection<T>
untuk beberapaT
, Anda dapat menerapkannya sebagai berikut:Catatan:
SingleOrArrayListConverter
,TCollection
jenisnya harus baca / tulis dan memiliki konstruktor tanpa parameter.Demo biola dengan tes unit dasar di sini .
sumber
Saya memiliki masalah yang sangat mirip. Permintaan Json saya sama sekali tidak saya kenal. Saya hanya tahu.
Akan ada objectId di dalamnya dan beberapa pasangan nilai kunci anonim DAN array.
Saya menggunakannya untuk Model EAV yang saya lakukan:
Permintaan JSON Saya:
Kelas saya yang saya tentukan:
dan sekarang saya ingin deserialize atribut yang tidak diketahui dengan nilai dan array di dalamnya Konverter saya terlihat seperti itu:
Jadi sekarang setiap kali saya mendapatkan AnonymObject saya dapat beralih melalui Kamus dan setiap kali ada Bendera saya "ValueDummyForEAV" saya beralih ke daftar, membaca baris pertama dan membagi nilainya. Setelah itu saya menghapus entri pertama dari daftar dan melanjutkan iterasi dari Kamus.
Mungkin seseorang memiliki masalah yang sama dan dapat menggunakan ini :)
Salam Andre
sumber
Anda dapat menggunakan
JSONConverterAttribute
seperti yang ditemukan di sini: http://james.newtonking.com/projects/json/help/Menganggap Anda memiliki kelas yang mirip
Anda akan mendekorasi properti kategori seperti yang terlihat di sini:
sumber
Untuk menangani ini, Anda harus menggunakan JsonConverter kustom. Tapi Anda mungkin sudah memikirkannya. Anda hanya mencari konverter yang dapat Anda gunakan segera. Dan ini menawarkan lebih dari sekedar solusi untuk situasi yang dijelaskan. Saya memberi contoh dengan pertanyaan yang diajukan.
Cara menggunakan konverter saya:
Tempatkan Atribut JsonConverter di atas properti.
JsonConverter(typeof(SafeCollectionConverter))
Dan ini konverter saya:
Dan konverter ini menggunakan kelas berikut:
Apa tepatnya yang dilakukannya? Jika Anda menempatkan atribut konverter, konverter akan digunakan untuk properti ini. Anda dapat menggunakannya pada objek normal jika Anda mengharapkan array json dengan 1 atau tanpa hasil. Atau Anda menggunakannya di
IEnumerable
tempat Anda mengharapkan objek json atau array json. (Ketahuilah bahwaarray
-object[]
- adalah anIEnumerable
) Kerugiannya adalah bahwa konverter ini hanya dapat ditempatkan di atas properti karena dia pikir dia dapat mengubah semuanya. Dan berhati-hatilah . Astring
juga merupakanIEnumerable
.Dan itu menawarkan lebih dari satu jawaban untuk pertanyaan: Jika Anda mencari sesuatu dengan id Anda tahu bahwa Anda akan mendapatkan kembali array dengan satu atau tanpa hasil. Itu
ToObjectCollectionSafe<TResult>()
Metode dapat menangani untuk Anda.Ini dapat digunakan untuk Hasil Tunggal vs Larik menggunakan JSON.net dan menangani satu item dan larik untuk properti yang sama dan dapat mengonversi larik menjadi satu objek.
Saya membuat ini untuk permintaan REST di server dengan filter yang mengembalikan satu hasil dalam array tetapi ingin mendapatkan hasilnya kembali sebagai objek tunggal dalam kode saya. Dan juga untuk respons hasil OData dengan hasil yang diperluas dengan satu item dalam array.
Bersenang-senanglah dengannya.
sumber
Saya menemukan solusi lain yang dapat menangani kategori sebagai string atau array dengan menggunakan objek. Dengan cara ini saya tidak perlu mengacaukan serializer json.
Tolong perhatikan jika Anda punya waktu dan beri tahu saya apa yang Anda pikirkan. https://github.com/MarcelloCarreira/sendgrid-csharp-eventwebhook
Ini didasarkan pada solusi di https://sendgrid.com/blog/tracking-email-using-azure-sendgrid-event-webhook-part-1/ tetapi saya juga menambahkan konversi tanggal dari stempel waktu, meningkatkan variabel untuk mencerminkan model SendGrid saat ini (dan membuat kategori berfungsi).
Saya juga membuat penangan dengan autentikasi dasar sebagai opsi. Lihat file ashx dan contohnya.
Terima kasih!
sumber