Bagian pertama artikel memeriksa mekanisme parsing objek JSON dengan struktur yang berubah secara dinamis. Objek-objek ini dilemparkan ke jenis namespace newtonsoft.json.linq, dan kemudian dikonversi ke struktur bahasa C #. Dalam komentar di bagian pertama ada banyak kritik, sebagian besar dibenarkan. Pada bagian kedua saya akan mencoba memperhitungkan semua komentar dan saran.

Selanjutnya, kami akan fokus pada mempersiapkan kelas untuk penyempurnaan yang lebih baik dari transformasi data, tetapi pertama-tama Anda harus kembali ke penguraian JSON itu sendiri. Saya ingat di bagian pertama, metode Parse () dan ToObject <T> () dari kelas JObject dan JArray dari namespace newtonsoft.json.linq digunakan:
HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; HttpResponseMessage response = (await httpClient.GetAsync(request)).EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); JObject jObject = JObject.Parse(responseBody); Dictionary<string, List<Order>> dict = jObject.ToObject<Dictionary<string, List<Order>>>();
Perlu dicatat bahwa dalam namespace newtonsof.json di kelas JsonConvert ada metode statis DeserializeObject <>, yang memungkinkan Anda untuk mengkonversi string secara langsung ke struktur C # yang sesuai dengan objek dan array notasi JSON:
Dictionary<string, List<Order>> JsonObject = JsonConvert.DeserializeObject<Dictionary<string, List<Order>>>(responseBody); List<string> Json_Array = JsonConvert.DeserializeObject<List<string>>(responseBody);
Dan di masa depan, metode ini akan digunakan dalam artikel, jadi Anda perlu menambahkan menggunakan newtonsoft.json ke program.
Selain itu, dimungkinkan untuk mengurangi lebih lanjut jumlah konversi antara - setelah menginstal perpustakaan Microsoft.AspNet.WebApi.Client (juga tersedia melalui NuGet), data dapat diuraikan langsung dari aliran menggunakan metode ReadAsAsync:
Dictionary<string, List<Order>> JsonObject = await (await httpClient.GetAsync(request)). EnsureSuccessStatusCode().Content. ReadAsAsync<Dictionary<string, List<Order>>>();
Terima kasih atas tip
sarangnya .
Mempersiapkan kelas untuk konversi
Kembali ke kelas Pesanan kami:
class Order { public int trade_id { get; set; } public string type { get; set; } public double quantity { get; set; } public double price { get; set; } public double amount { get; set; } public int date { get; set; } }
Biarkan saya mengingatkan Anda bahwa itu dibuat berdasarkan format yang diusulkan oleh JSON C # Class Generator. Ada dua poin yang ketika bekerja dengan objek dari kelas ini dapat menyebabkan kesulitan.
Yang pertama - dalam formulir ini, properti dari kelas kami melanggar aturan untuk bidang penamaan. Selain itu, logis untuk objek bertipe Order untuk berharap bahwa pengenalnya akan disebut OrderID (dan bukan traid_id, seperti yang terjadi pada contoh dari bagian pertama). Untuk mengaitkan elemen struktur JSON dan properti kelas dengan nama arbitrer, Anda harus menambahkan atribut JsonProperty sebelum properti:
class Order { [JsonProperty("trade_id")] public int OrderID { get; set; } [JsonProperty("type")] public string Type { get; set; } [JsonProperty("quantity")] public double Quantity { get; set; } [JsonProperty("price")] public double Price { get; set; } [JsonProperty("amount")] public double Amount { get; set; } [JsonProperty("date")] public int Date { get; set; } }
Akibatnya, nilai yang sesuai dengan elemen "trade_id" akan dicatat di properti OrderID, "ketik" di Tipe, dll. Atribut JsonProperty juga diperlukan untuk properti bersambung / deserialisasi yang memiliki pengubah pribadi atau statis - secara default, properti tersebut diabaikan.
Akibatnya, kode untuk menyusun daftar semua transaksi OrderID untuk pasangan mata uang BTC_USD dan ETH_USD mungkin terlihat seperti ini:
using HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; string responseBody = await (await httpClient.GetAsync(request)). EnsureSuccessStatusCode(). Content.ReadAsStringAsync(); Dictionary<string, List<Order>> PairList = JsonConvert.DeserializeObject<Dictionary<string, List<Order>>>(responseBody); List<int> IDs = new List<int>(); foreach (var pair in PairList) foreach (var order in pair.Value) IDs.Add(order.OrderID);
Kesulitan kedua ketika bekerja dengan kelas ini adalah properti Date. Seperti yang Anda lihat, Generator JSON C # Class mendefinisikan elemen "date" sebagai bilangan bulat utama. Tetapi akan jauh lebih nyaman jika properti Date dari kelas kami memiliki tipe yang khusus dibuat untuk tanggal - DateTime. Cara melakukan ini akan dijelaskan nanti.
Fitur bekerja dengan tanggal
Frasa awal
artikel dalam dokumentasi untuk newtonsof.json, dengan deskripsi bekerja dengan tanggal, dapat secara kasar diterjemahkan sebagai "DateTime di JSON - sulit". Masalahnya adalah bahwa spesifikasi JSON itu sendiri tidak mengandung informasi tentang sintaksis mana yang harus digunakan untuk menggambarkan tanggal dan waktu.
Semuanya relatif baik ketika tanggal dalam string JSON disajikan dalam bentuk teks dan format presentasi sesuai dengan salah satu dari tiga opsi: "Microsoft" (saat ini dianggap usang), "JavaScript" (waktu
Unix ) dan varian
ISO 8601 . Contoh format yang valid:
Dictionary<string, DateTime> d = new Dictionary<string, DateTime> { { "date", DateTime.Now } }; string isoDate = JsonConvert.SerializeObject(d);
Namun, dalam kasus pertukaran Exmo, semuanya sedikit lebih rumit. Deskripsi API pertukaran menunjukkan bahwa tanggal dan waktu ditentukan dalam format Unix (JavaScript). Dan, secara teoritis, menambahkan ke properti Date kita dari kelas Order fungsi konversi format dari kelas JavaScriptDateTimeConverter (), kita harus mendapatkan pemeran tanggal ke tipe DateTime:
class Order { [JsonProperty("trade_id")] public int OrderID { get; set; } [JsonProperty("type")] public string Type { get; set; } [JsonProperty("quantity")] public double Quantity { get; set; } [JsonProperty("price")] public double Price { get; set; } [JsonProperty("amount")] public double Amount { get; set; } [JsonProperty("date", ItemConverterType = typeof(JavaScriptDateTimeConverter))] public DateTime Date { get; set; } }
Namun, dalam hal ini, ketika Anda mencoba mem-parsing data ke dalam variabel tipe DateTime, pengecualian Newtonsoft.Json.JsonReaderException sudah akrab dari bagian pertama artikel. Ini terjadi karena fungsi konversi kelas JavaScriptDateTimeConverter tidak dapat mengonversi data numerik ke tipe DateTime (ini hanya berfungsi untuk string).

Cara keluar yang mungkin dari situasi ini adalah dengan menulis kelas konversi format Anda sendiri. Bahkan, kelas semacam itu sudah ada dan dapat digunakan dengan menghubungkan namespace Newtonsoft.Json.Converters terlebih dahulu (perhatikan bahwa fungsi terbalik tidak untuk mengkonversi dari DateTime ke JSON di kelas ini):
class UnixTimeToDatetimeConverter : DateTimeConverterBase { private static readonly DateTime _epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.Value == null) { return null; } return _epoch.AddSeconds(Convert.ToDouble(reader.Value)).ToLocalTime(); } }
Tetap hanya untuk menghubungkan fungsi kami ke properti Date dari kelas Order. Untuk melakukan ini, gunakan atribut JsonConverter:
class Order { [JsonProperty("trade_id")] public int OrderID { get; set; } [JsonProperty("type")] public string Type { get; set; } [JsonProperty("quantity")] public double Quantity { get; set; } [JsonProperty("price")] public double Price { get; set; } [JsonProperty("amount")] public double Amount { get; set; } [JsonProperty("date")] [JsonConverter(typeof(UnixTimeToDatetimeConverter))] public DateTime Date { get; set; } }
Sekarang properti Date kami bertipe DateTime dan kami dapat, misalnya, membuat daftar penawaran dalam 10 menit terakhir:
HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; string responseBody = await (await httpClient.GetAsync(request)). EnsureSuccessStatusCode(). Content.ReadAsStringAsync(); Dictionary<string, List<Order>> PairList = JsonConvert.DeserializeObject<Dictionary<string, List<Order>>>(responseBody); List<Order> Last10minuts = new List<Order>(); foreach (var pair in PairList) foreach (var order in pair.Value) if (order.Date > DateTime.Now.AddMinutes(-10)) Last10minuts.Add(order);
Teks lengkap program using System; using System.Threading.Tasks; using System.Collections.Generic; using System.Net.Http; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; namespace JSONObjects { class Order { [JsonProperty("pair")] public string Pair { get; set; } [JsonProperty("trade_id")] public int OrderID { get; set; } [JsonProperty("type")] public string Type { get; set; } [JsonProperty("quantity")] public double Quantity { get; set; } [JsonProperty("price")] public double Price { get; set; } [JsonProperty("amount")] public double Amount { get; set; } [JsonProperty("date")] [JsonConverter(typeof(UnixTimeToDatetimeConverter))] public DateTime Date { get; set; } } class UnixTimeToDatetimeConverter : DateTimeConverterBase { private static readonly DateTime _epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.Value == null) { return null; } return _epoch.AddSeconds(Convert.ToDouble(reader.Value)).ToLocalTime(); } } class Program { public static async Task Main(string[] args) { using HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; string responseBody = await (await httpClient.GetAsync(request)). EnsureSuccessStatusCode(). Content.ReadAsStringAsync(); Dictionary<string, List<Order>> PairList = JsonConvert. DeserializeObject<Dictionary<string, List<Order>>>(responseBody); List<Order> Last10minuts = new List<Order>(); foreach (var pair in PairList) foreach (var order in pair.Value) if (order.Date > DateTime.Now.AddMinutes(-10)) Last10minuts.Add(order); } } }
Substitusi Elemen JSON
Sebelumnya kami bekerja dengan tim pertukaran perdagangan. Perintah ini mengembalikan objek dengan bidang-bidang berikut:
public class BTCUSD { public int trade_id { get; set; } public string type { get; set; } public string quantity { get; set; } public string price { get; set; } public string amount { get; set; } public int date { get; set; } }
Perintah pertukaran user_open_orders mengembalikan struktur yang sangat mirip:
public class BTCUSD { public string order_id { get; set; } public string created { get; set; } public string type { get; set; } public string pair { get; set; } public string quantity { get; set; } public string price { get; set; } public string amount { get; set; } }
Oleh karena itu, masuk akal untuk mengadaptasi kelas Order sehingga dapat mengonversi tidak hanya data perintah perdagangan, tetapi juga data perintah user_open_orders.
Perbedaannya adalah bahwa ada elemen pasangan baru yang berisi nama pasangan mata uang, trade_id digantikan oleh order_id (dan sekarang ini adalah string), dan tanggal telah dibuat dan juga sebuah string.
Untuk mulai dengan, kami menambahkan kemampuan untuk menyimpan bidang order_id dan dibuat di bidang OrderID dan Tanggal yang sesuai dari kelas Pesanan. Untuk melakukan ini, siapkan kelas OrderDataContractResolver, di mana nama-nama bidang akan diganti untuk penguraian (System.Reflection dan Newtonsoft.Json.Nama spasi spasiasi akan diperlukan):
class OrderDataContractResolver : DefaultContractResolver { public static readonly OrderDataContractResolver Instance = new OrderDataContractResolver(); protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); if (property.DeclaringType == typeof(Order)) { if (property.PropertyName.Equals("trade_id", StringComparison.OrdinalIgnoreCase)) { property.PropertyName = "order_id"; } if (property.PropertyName.Equals("date", StringComparison.OrdinalIgnoreCase)) { property.PropertyName = "created"; } } return property; } }
Selanjutnya, kelas ini harus ditentukan sebagai parameter metode DeserializeObject sebagai berikut:
Dictionary<string, List<Order>> PairList = JsonConvert.DeserializeObject<Dictionary<string, List<Order>>>(responseBody, new JsonSerializerSettings { ContractResolver = OrderDataContractResolver.Instance });
Akibatnya, struktur JSON tersebut diperoleh sebagai respons terhadap perintah user_open_orders:
{"BTC_USD":[{"order_id":"4722868563","created":"1577349229","type":"sell","pair":"BTC_USD","quantity":"0.002","price":"8362.2","amount":"16.7244"}]}
akan dikonversi ke struktur data seperti itu:

Harap perhatikan bahwa agar program berfungsi dengan benar, tipe bidang OrderID di kelas Order harus diganti dengan string.
Teks lengkap program using System; using System.Threading.Tasks; using System.Collections.Generic; using System.Net.Http; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System.Reflection; namespace JSONObjects { class Order { [JsonProperty("pair")] public string Pair { get; set; } [JsonProperty("trade_id")] public string OrderID { get; set; } [JsonProperty("type")] public string Type { get; set; } [JsonProperty("quantity")] public double Quantity { get; set; } [JsonProperty("price")] public double Price { get; set; } [JsonProperty("amount")] public double Amount { get; set; } [JsonProperty("date")] [JsonConverter(typeof(UnixTimeToDatetimeConverter))] public DateTime Date { get; set; } } class OrderDataContractResolver : DefaultContractResolver { public static readonly OrderDataContractResolver Instance = new OrderDataContractResolver(); protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); if (property.DeclaringType == typeof(Order)) { if (property.PropertyName.Equals("trade_id", StringComparison.OrdinalIgnoreCase)) { property.PropertyName = "order_id"; } if (property.PropertyName.Equals("date", StringComparison.OrdinalIgnoreCase)) { property.PropertyName = "created"; } } return property; } } class UnixTimeToDatetimeConverter : DateTimeConverterBase { private static readonly DateTime _epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.Value == null) { return null; } return _epoch.AddSeconds(Convert.ToDouble(reader.Value)).ToLocalTime(); } } class Program { public static async Task Main(string[] args) { using HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; string responseBody = await (await httpClient.GetAsync(request)). EnsureSuccessStatusCode(). Content.ReadAsStringAsync(); Dictionary<string, List<Order>> PairList = new Dictionary<string, List<Order>>(); JObject jObject = JObject.Parse(responseBody); foreach (var pair in jObject) { List<Order> orders = new List<Order>(); foreach (var order in pair.Value.ToObject<List<Order>>()) { order.Pair = pair.Key; orders.Add(order); } PairList.Add(pair.Key, orders); } responseBody = "{\"BTC_USD\":[{\"order_id\":\"4722868563\"," + "\"created\":\"1577349229\",\"type\":\"sell\"," + "\"pair\":\"BTC_USD\",\"quantity\":\"0.002\"," + "\"price\":\"8362.2\",\"amount\":\"16.7244\"}]}"; Dictionary<string, List<Order>> PairList1 = JsonConvert.DeserializeObject<Dictionary<string, List<Order>>> (responseBody, new JsonSerializerSettings { ContractResolver = OrderDataContractResolver.Instance }); } } } }
Seperti yang Anda lihat, ketika perintah user_open_orders dipanggil, jawabannya berisi bidang "pasangan", dalam kasus perintah perdagangan, informasi tentang pasangan mata uang hanya terkandung dalam nilai kunci. Oleh karena itu, Anda harus mengisi bidang Pair setelah parsing:
foreach (var pair in PairList) foreach (var order in pair.Value) order.Pair = pair.Key;
Atau gunakan objek JObject:
Dictionary<string, List<Order>> PairList = new Dictionary<string, List<Order>>(); JObject jObject = JObject.Parse(responseBody); foreach (var pair in jObject) { List<Order> orders = new List<Order>(); foreach (var order in pair.Value.ToObject<List<Order>>()) { order.Pair = pair.Key; orders.Add(order); } PairList.Add(pair.Key, orders); }
Yang pada akhirnya mengarah pada pembuatan struktur data berikut:
