Arbeiten mit der Newtonsoft.Json-Bibliothek anhand eines realen Beispiels. Teil 1

Wenn Sie diesen Artikel lesen, sind Sie sich höchstwahrscheinlich dessen bewusst, was JSON ist, und das folgende Bild ist Ihnen vertraut. In jedem Fall empfehle ich Ihnen, diese Seite zu besuchen, wenn Sie noch nicht dort waren. Bevor Sie diese Seite lesen, sollten Sie sich zum Beispiel unter diesem Link mit den allgemeinen Prinzipien der Arbeit mit der JSON-Notation in C # vertraut machen.



Ich möchte darauf hinweisen, dass dieser Hinweis keine vollständige Offenlegung des Themas vorgibt. Der Zweck dieses Textes besteht darin, die Entwicklungen zu strukturieren und beizubehalten, die ich bei der Arbeit mit der Newtonsoft.Json-Bibliothek verwendet habe.

Erklärung des Problems


Im Rahmen des Artikels ist es im Großen und Ganzen nicht so wichtig, wie die ursprünglichen Daten für das Parsen erhalten wurden. Diese Erklärung wird jedoch mit Sicherheit die Wahrnehmung des Materials erleichtern. Die Hauptaufgabe besteht also darin, die Funktionen der EXMO-Cryptoexchange- API zu implementieren. Der Einfachheit halber werden wir hauptsächlich mit der öffentlichen Schnittstelle (Public API) arbeiten. In diesem Fall sieht die Informationsanfrage wie eine normale Seitenadresse einer Site aus. Zum Senden von Anforderungen wird die HttpClient-Klasse des System.Net.Http-Namespace verwendet:

HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD"; HttpResponseMessage response = (await httpClient.GetAsync(request)).EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); 

Das Ergebnis dieses Programms können Sie sehen, indem Sie auf den Link https://api.exmo.com/v1/trades/?pair=BTC_USD klicken. Wenn Sie diese Daten in den JSON C # -Klassengenerator laden, wird uns die folgende Klassenstruktur zur Deserialisierung angeboten:

  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; } } public class RootObject { public List<BTCUSD> BTC_USD { get; set; } } 

Es ist zu beachten, dass Sie mit der Crypto-Exchange-Funktion "Trades" selbst Informationen zu mehreren Währungspaaren gleichzeitig anfordern können. Und im Falle einer Anfrage

 string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; 

Die Klassenstruktur sieht schon so aus:

  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; } } public class ETHUSD { 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; } } public class RootObject { public List<BTCUSD> BTC_USD { get; set; } public List<ETHUSD> ETH_USD { get; set; } } 

Natürlich können Sie keine Klassen für jede Kombination von Währungspaaren vorbereiten, was bedeutet, dass Sie irgendwie aussteigen müssen.

Wie es geht, ist nicht notwendig.
Überraschenderweise ist es fast unmöglich, im russischsprachigen Internet gute Artikel zum Arbeiten mit der JSON-Notation zu finden. Infolgedessen enthalten die EXMO-API-Implementierungen, die auf GitHub zu finden sind, Manipulationen mit der ursprünglichen Datenstruktur unter Verwendung von split s, trim s und dergleichen.

Ich muss zugeben, dass ich auch zuerst ein Fahrrad erfunden und das Parsen selbst durchgeführt habe. Trotz der Tatsache, dass der Code absolut funktioniert, ist dies ein großartiges Beispiel dafür, wie Sie es NICHT tun können.


 LinkedList<OrderInform> loi = new LinkedList<OrderInform>(); try { string[] json = answer.Split(new char[] { '{', '}' }); foreach (var item in json) { if (item.Length > 20) { string[] trade = item.Split(new char[] { ',' }); if (trade.Length > 5) { string t_id = trade[0].Split(new char[] { ':' })[1].Trim('"'); string t_type = trade[1].Split(new char[] { ':' })[1].Trim('"'); string t_quantity = trade[2].Split(new char[] { ':' })[1].Trim('"') .Replace(".", ","); string t_price = trade[3].Split(new char[] { ':' })[1].Trim('"') .Replace(".", ","); string t_amount = trade[4].Split(new char[] { ':' })[1].Trim('"') .Replace(".", ","); string t_date_time = trade[5].Split(new char[] { ':' })[1].Trim('"'); loi.AddLast(new OrderInform(pair, Convert.ToInt32(t_id), t_type, Convert.ToDouble(t_quantity), Convert.ToDouble(t_price), Convert.ToDouble(t_amount), (new DateTime(1970, 1, 1, 0, 0, 0, 0)) .AddSeconds(Convert.ToInt32(t_date_time)))); } } } return loi; } catch (Exception ex) { return new LinkedList<OrderInform>(); } 


JSON-Objekte (JObject-Klasse)


Das JSON-Objekt ist eine ungeordnete Menge von Schlüssel / Wert-Paaren. Ein Objekt beginnt mit '{' (eine öffnende geschweifte Klammer) und endet mit '}' (eine schließende geschweifte Klammer). Auf jeden Namen folgt: (ein Doppelpunkt), Schlüssel / Wert-Paare werden durch ein Komma getrennt.



Aus der zuvor von JSON C # Class Generator vorgeschlagenen Struktur geht hervor, dass wir für jedes Währungspaar 6 konstante Felder haben. Wickeln Sie sie in eine separate Klasse ein und nennen Sie sie Order.

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

Um eine Reihe dieser Felder aus der sich dynamisch ändernden Datenstruktur des Datenaustauschs abrufen zu können, müssen Sie die in der newtosoft.json-Bibliothek verfügbaren Datentypen genauer kennen. Genauer gesagt im Namespace newtonsoft.json.linq.

Um mit der Bibliothek arbeiten zu können, müssen Sie sie installieren.
Um die Bibliothek in Visual Studio zu installieren, können Sie einfach NuGet durchsuchen.
Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf die Projektmappe und wählen Sie "NuGet-Pakete für Projektmappe verwalten ..." (NuGet-Pakete für Projektmappe verwalten ...).



Klicken Sie anschließend auf "Durchsuchen" und geben Sie newtosoft.json in die Suchleiste ein. Wir setzen ein Häkchen vor unserer Lösung und klicken auf "Installieren" ("Install").



Nach der Installation der Bibliothek steht der Namespace newtosoft.json.linq für die Verbindung zum Projekt zur Verfügung:

 using Newtonsoft.Json.Linq; 

Newtosoft.json.linq verwendet die abstrakte JToken- Klasse zur Darstellung von Daten, von denen die Klassen JValue (zur Darstellung einfacher Werte) und JContainer (zur Darstellung von Strukturen) geerbt werden. Strukturen können wiederum ein JArray (Array), ein JConstructor (Konstruktor), ein JObject (Objekt) oder eine JProperty (Eigenschaft) sein.

Die JArray- und JObject-Klassen verfügen über eine Parse-Methode, mit der Sie JSON-Daten von einer regulären Zeichenfolge in die entsprechenden Objekte konvertieren können. Wenn wir also die JObject.Parse (String) -Methode für die Datenstruktur der Exmo-Trades-Funktion verwenden:

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

Wir werden eine Struktur von zwei Elementen sehen, von denen jedes ein Schlüssel-Wert-Paar darstellt. In diesem Fall ist der Schlüssel der Name des Währungspaares (Typ String) und der Wert die Liste der Transaktionen (Typ JToken):



Nun stehen uns die Transaktionslisten mit den Schlüsseln "BTC_USD" und "USD_ETH" zur Verfügung. Zum Beispiel Transaktionen auf dem Währungspaar "BTC_USD":



Dann müssen wir die Daten nur noch in einen für die Arbeit geeigneten Typ übersetzen Liste <Reihenfolge> Verwenden der ToObject <> () -Methode:

 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); JToken list = jObject["BTC_USD"]; List<Order> trades = list.ToObject<List<Order>>(); 

entweder konvertieren Sie die gesamte Struktur in Typ Dictionary <string, List <Order >> :

 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>> trades = new Dictionary<string, List<Order>>(); foreach (KeyValuePair<string, JToken> obj in jObject) trades.Add(obj.Key, obj.Value.ToObject<List<Order>>()); 

JSON-Arrays (JArray-Klasse)


Das JSON-Array ist eine geordnete Sammlung von Werten. Ein Array beginnt mit '[' (der öffnenden eckigen Klammer) und endet mit ']' (der schließenden eckigen Klammer). Werte werden durch Komma getrennt.

Wie das JSON-Array aussieht, können Sie über den Link https://api.exmo.com/v1/currency einsehen . Wenn Sie versuchen, es in ein JObject zu konvertieren, wie wir es bei der vorherigen Anfrage getan haben:

 HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/currency"; HttpResponseMessage response = (await httpClient.GetAsync(request)).EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); JObject jObject = JObject.Parse(responseBody); 

Wir erhalten eine Newtonsoft.Json.JsonReaderException-Ausnahme, die uns mitteilt, dass die konvertierten Daten kein Objekt sind.



Arrays müssen in einen speziellen JArray-Typ konvertiert werden, der wie JObject von JContainer erbt.

 HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/currency"; HttpResponseMessage response = (await httpClient.GetAsync(request)).EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); JObject jObject = JObject.Parse(responseBody); JArray jArray = JArray.Parse(responseBody); 

Falls beim Parsen die ursprüngliche Struktur nicht im Voraus bekannt ist oder die Fähigkeit zur Verarbeitung beider Datentypen erforderlich ist - Sie können JContainer oder JToken als Typ des konvertierten Objekts angeben, dann werden die Daten beim Parsen implizit in den gewünschten Typ konvertiert.

 HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/currency"; HttpResponseMessage response = (await httpClient.GetAsync(request)).EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); JToken jArray = JToken.Parse(responseBody ); //    JArray List<string> list = jArray.ToObject<List<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(); JToken jObject = JToken.Parse(responseBody ); //    JObject Dictionary<string, List<Order>> dict = jObject.ToObject<Dictionary<string, List<Order>>>(); 

JSON-Werte (JValue-Klasse)


Gemäß der JSON-Formatbeschreibung kann der Wert eine Zeichenfolge in doppelten Anführungszeichen, eine Zahl, true, false, null, ein Objekt oder ein Array sein . Diese Strukturen können verschachtelt werden. In der Implementierung von newotnsoft.json können nur Objekte einfachen Typs einen Wert haben. Der Versuch, ein JArray in einen JValue-Wert zu konvertieren, führt zur Ausnahme "System.InvalidCastException".

Als einführendes Material zum Arbeiten mit bestimmten Werten (der JValue-Klasse) in der newtonsoft.json-Bibliothek eignen sich Beispiele aus der Dokumentation ein- und zweimal .

Anwendungsbeispiel für Daten aus dem Austausch:

 HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/currency"; HttpResponseMessage response = (await httpClient.GetAsync(request)).EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); JArray jArray = JArray.Parse(responseBody ); foreach (JValue value in jArray) { string s = (string)value; } 

Im zweiten Teil des Artikels werde ich die Möglichkeiten zur Vorbereitung einer Klasse für das Parsen von Daten sowie einige Funktionen zum Arbeiten mit dem Datentyp DateTime genauer beschreiben.

Der vollständige Text des Programms.
 using System.Threading.Tasks; using System.Collections.Generic; using System.Net.Http; using Newtonsoft.Json.Linq; namespace JSONObjects { 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; } } class Program { public static async Task Main(string[] args) { HttpClient httpClient = new HttpClient(); string request = "https://api.exmo.com/v1/currency"; HttpResponseMessage response = (await httpClient.GetAsync(request)).EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); JToken jArray = JToken.Parse(responseBody); //    JArray List<string> list = jArray.ToObject<List<string>>(); request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; response = (await httpClient.GetAsync(request)).EnsureSuccessStatusCode(); responseBody = await response.Content.ReadAsStringAsync(); JToken jObject = JToken.Parse(responseBody); //    JObject Dictionary<string, List<Order>> dict = jObject.ToObject<Dictionary<string, List<Order>>>(); } } } 


Die vollständigsten und detailliertesten Informationen über die Bibliothek finden Sie immer in der offiziellen Dokumentation .

Source: https://habr.com/ru/post/de481514/


All Articles