English
preview
JSON beherrschen: Erstellen Sie Ihren eigenen JSON-Reader in MQL5 von Grund auf

JSON beherrschen: Erstellen Sie Ihren eigenen JSON-Reader in MQL5 von Grund auf

MetaTrader 5Integration | 10 Juni 2025, 08:37
59 0
Sahil Bagdi
Sahil Bagdi

Einführung

Hallo und herzlich willkommen! Wenn Sie jemals versucht haben, JSON-Daten in MQL5 zu parsen oder zu manipulieren, haben Sie sich vielleicht gefragt, ob es dafür einen einfachen und flexiblen Ansatz gibt. JSON, die Abkürzung für JavaScript Object Notation, hat als leichtes Datenaustauschformat, das sowohl für Menschen als auch für Maschinen lesbar ist, an Beliebtheit gewonnen. Während MQL5 in erster Linie für die Erstellung von Expert Advisors, Indikatoren und Skripten für die MetaTrader 5-Plattform bekannt ist, verfügt es nicht über eine native JSON-Bibliothek. Das bedeutet, dass Sie, wenn Sie mit JSON-Daten arbeiten möchten – sei es von einer Web-API, einem externen Server oder aus Ihren eigenen lokalen Dateien – wahrscheinlich eine nutzerdefinierte Lösung entwickeln oder eine vorhandene Bibliothek integrieren müssen.

In diesem Artikel möchten wir diese Lücke schließen, indem wir zeigen, wie Sie Ihren eigenen JSON-Leser in MQL5 erstellen können. Auf dem Weg dorthin werden wir die grundlegenden Konzepte des Parsens von JSON erkunden und die Erstellung einer flexiblen Klassenstruktur durchgehen, die verschiedene JSON-Elementtypen (wie Objekte, Arrays, Strings, Zahlen, Boolesche Werte und Nullwerte) verarbeiten kann. Unser Ziel ist es, Sie in die Lage zu versetzen, JSON-Strings bequem zu parsen und auf die darin enthaltenen Daten zuzugreifen oder sie zu ändern – und das alles in Ihrer MetaTrader 5-Umgebung.

Wir werden eine ähnliche Struktur wie in anderen MQL5-bezogenen Artikeln verfolgen, aber mit einem speziellen Fokus auf JSON-Parsing und -Nutzung. Dieser Artikel ist in fünf Hauptabschnitte unterteilt: eine Einführung (die Sie gerade lesen), ein tieferes Eintauchen in die Grundlagen von JSON und wie es in MQL5 passt, eine Schritt-für-Schritt-Anleitung zum Aufbau eines grundlegenden JSON-Parsers von Grund auf, eine Erkundung fortgeschrittener Funktionen für die JSON-Behandlung und schließlich ein umfassendes Code-Listing mit abschließenden Gedanken.

JSON ist überall. Ganz gleich, ob Sie Marktdaten von einem Drittanbieterdienst abrufen, Ihre eigenen Handelsdatensätze hochladen oder mit komplexen Strategien experimentieren, die eine dynamische Konfiguration erfordern, JSON bleibt ein nahezu universelles Format. Einige der häufigsten praktischen Anwendungsfälle für JSON in der Welt des algorithmischen Handels sind:

  1. Abruf von Marktdaten: Viele moderne Broker-APIs oder Finanzdatendienste bieten Echtzeit- oder historische Daten in JSON an. Mit einem JSON-Reader können Sie diese Daten schnell auswerten und in Ihre Handelsstrategie integrieren.

  2. Strategie-Konfiguration: Angenommen, Sie haben einen Expert Advisor, der mehrere Parameter unterstützt: maximaler Spread, gewünschtes Kontorisiko oder erlaubte Handelszeiten. Eine JSON-Datei kann diese Einstellungen sauber speichern, und ein JSON-Leser in MQL5 kann diese Parameter dynamisch laden oder aktualisieren, ohne Ihren Code neu zu kompilieren.

  3. Versenden von Protokollen oder Daten: In bestimmten Konstellationen möchten Sie vielleicht Ihre Handelsprotokolle oder Debug-Meldungen zu Analysezwecken an einen externen Server übertragen. Das Versenden als JSON kann dazu beitragen, dass Ihre Protokolle konsistent, leicht analysierbar und in Tools integrierbar sind, die strukturierte Daten erwarten.

Viele Online-Beispiele zeigen, wie man JSON in Sprachen wie Python, JavaScript oder C++ auseinandernimmt. MQL5 ist jedoch eine spezialisierte Sprache mit ihren eigenen Beschränkungen. Das bedeutet, dass wir auf bestimmte Aspekte achten müssen: Speicherverwaltung, Array-Verwendung, strenge Datentypen usw.

Wir werden eine nutzerdefinierte Klasse (oder eine Reihe von Klassen) erstellen, die sich mit JSON-Parsing und -Manipulation befasst. Die Idee ist, es so zu gestalten, dass Sie etwas tun können wie:

CMyJsonParser parser;
parser.LoadString("{\"symbol\":\"EURUSD\",\"lots\":0.1,\"settings\":{\"slippage\":2,\"retries\":3}}");

// Access top-level fields:
Print("Symbol = ", parser.GetObject("symbol").ToStr());
Print("Lots = ", parser.GetObject("lots").ToDbl());

// Access nested fields:
CMyJsonElement settings = parser.GetObject("settings");
Print("Slippage = ", settings.GetObject("slippage").ToInt());
Print("Retries = ", settings.GetObject("retries").ToInt());

Natürlich kann Ihr endgültiger Ansatz in Bezug auf Benennung oder Struktur leicht abweichen, aber diese Art von Nutzerfreundlichkeit ist das Ziel. Durch den Aufbau eines robusten Parsers haben Sie eine Grundlage für Erweiterungen wie die Umwandlung von MQL5-Datenstrukturen in JSON für die Ausgabe oder das Hinzufügen von Caching-Logik für wiederholte JSON-Abfragen.

Vielleicht sind Sie schon auf verschiedene JSON-Bibliotheken gestoßen, einschließlich einiger kurzer Skripte, die JSON durch die Verarbeitung von Zeichenarrays analysieren. Wir werden von diesen bestehenden Ansätzen lernen, aber den Code nicht direkt kopieren. Stattdessen konstruieren wir etwas Neues mit einer ähnlichen Idee, damit es für Sie leichter zu verstehen und zu pflegen ist. Wir werden unseren Code Teil für Teil zerlegen, und am Ende dieses Artikels werden Sie Zugang zu einer endgültigen, zusammenhängenden Implementierung haben, die Sie an Ihre eigenen Handelsprogramme anhängen können.

Wir hoffen, dass dieser Ansatz, eine Bibliothek von Grund auf aufzubauen und jedes Segment in einfacher Sprache zu erklären, Ihnen ein tieferes Verständnis vermittelt, als wenn wir Ihnen nur eine fertige Lösung geben würden. Indem Sie die Funktionsweise des Parsers verinnerlichen, können Sie ihn später leichter debuggen und anpassen.

Während JSON ein textbasiertes Format ist, können MQL5-Strings eine Vielzahl von Sonderzeichen enthalten, darunter Zeilenumbrüche, Wagenrückläufe oder Unicode-Zeichen. Unsere Implementierung wird einige dieser Nuancen berücksichtigen und versuchen, sie auf differenzierte Weise zu lösen. Vergewissern Sie sich jedoch immer, dass Ihre Eingabedaten gültiges JSON sind. Wenn Sie missgebildetes JSON oder zufälligen Text erhalten, der behauptet, gültig zu sein, müssen Sie wahrscheinlich eine robustere Fehlerbehandlung hinzufügen.

Hier ein kurzer Überblick über die Gliederung dieses Artikels:

  1. Abschnitt 1 (Sie sind hier!) – Einleitung
    Wir haben gerade besprochen, was JSON ist, warum es wichtig ist und wie wir einen nutzerdefinierten Parser in MQL5 schreiben werden. Dies ist die Grundlage für alles Weitere.

  2. Abschnitt 2 – Die Grundlagen: Grundlagen von JSON und MQL5
    Wir sehen uns die wichtigsten Strukturelemente von JSON an, ordnen sie dann den MQL5-Datentypen zu und zeigen, auf welche Aspekte wir besonders achten müssen.

  3. Abschnitt 3 – Erweiterung unseres Parsers um fortgeschrittene Funktionalitäten
    Hier werden wir über mögliche Erweiterungen oder Verbesserungen sprechen: wie man mit Arrays umgeht, wie man eine Fehlerprüfung hinzufügt und wie man MQL5-Daten zurück in JSON konvertiert, wenn man Daten nach außen senden muss.

  4. Abschnitt 4 – Vollständiger Kodex
    Zum Schluss fassen wir unsere gesamte Bibliothek an einem Ort zusammen, sodass Sie eine einzige Referenzdatei erhalten.

  5. Abschnitt 5 – Schlussfolgerung
    Wir fassen die wichtigsten Erkenntnisse zusammen und zeigen Ihnen einige nächste Schritte auf, die Sie bei Ihren eigenen Projekten in Betracht ziehen sollten.

Am Ende dieses Artikels werden Sie eine voll funktionsfähige JSON-Parsing- und Manipulationsbibliothek in MQL5 haben. Darüber hinaus werden Sie verstehen, wie das Ganze unter der Haube funktioniert, sodass Sie besser gerüstet sind, JSON in Ihre automatisierten Handelslösungen zu integrieren.


Die Grundlagen – JSON und MQL5-Grundlagen

Willkommen zurück! Nachdem wir nun den Gesamtplan für unseren nutzerdefinierten MQL5-JSON-Leser erstellt haben, ist es an der Zeit, die Feinheiten von JSON zu ergründen und zu sehen, wie diese auf MQL5 abgebildet werden. Wir werden uns mit der Struktur von JSON befassen, welche Datentypen am einfachsten zu parsen sind und mögliche Fallstricke aufzeigen, wenn wir JSON-Daten in MetaTrader 5 einbringen. Am Ende dieses Abschnitts werden Sie eine viel klarere Vorstellung davon haben, wie man JSON in einer MQL5-Umgebung anpackt, und damit die Voraussetzungen für die praktische Programmierung schaffen, die nun folgt.

JSON (JavaScript Object Notation) ist ein textbasiertes Format, das häufig für die Übertragung und Speicherung von Daten verwendet wird. Im Gegensatz zu XML ist es relativ leichtgewichtig: Daten werden in geschweifte Klammern ( {} ) für Objekte oder eckige Klammern ( [] ) für Arrays eingeschlossen, und jedes Feld wird in einfachen Schlüssel-Wert-Paaren dargestellt. Hier ist ein kleines Beispiel:

{
  "symbol": "EURUSD",
  "lots": 0.05,
  "enableTrade": true
}

Dies ist für einen Menschen leicht zu lesen und für eine Maschine einfach zu analysieren. Jede Information – wie „symbol“ oder „enableTrade“ – wird als Schlüssel bezeichnet, der einen Wert enthält. Der Wert kann eine Zeichenfolge, eine Zahl, ein Boolescher Wert oder sogar ein anderes verschachteltes Objekt oder Array sein. Kurz gesagt geht es bei JSON um die Organisation von Daten in einer verschachtelten Baumstruktur, mit der Sie alles darstellen können, von einfachen Parametern bis hin zu komplexeren hierarchischen Daten.

JSON versus MQL5-Datentypen:

  1. Zeichenketten: JSON-Zeichenfolgen erscheinen in doppelten Anführungszeichen, wie z. B. "Hello World" . In MQL5 gibt es auch den Typ String, aber diese Strings können Sonderzeichen, Escape-Sequenzen und Unicode enthalten. Die erste Schwierigkeit besteht also darin, sicherzustellen, dass unser Parser Anführungszeichen, escapete Symbole (wie \“ ) und möglicherweise Unicode-Codepunkte (z. B. \u00A9 ) korrekt verarbeitet.
  2. Zahlen: In JSON können Zahlen ganze Zahlen (wie 42) oder Dezimalzahlen (3.14159) sein. MQL5 speichert Zahlen hauptsächlich als int (für Ganzzahlen) oder double (für Fließkommazahlen). Allerdings werden nicht alle numerischen Werte in JSON sauber auf den Typ int abgebildet. So ist zum Beispiel 1234567890 gültig, aber in manchen Kontexten benötigen Sie in MQL5 möglicherweise den Typ long, wenn die Zahl wirklich groß ist. Wir müssen besonders aufpassen, wenn die JSON-Zahl außerhalb des Bereichs einer typischen 32-Bit-Ganzzahl liegt. Außerdem kann es notwendig sein, eine große Ganzzahl in einen Double zu konvertieren, wenn sie den Grenzwert einer Standard-Ganzzahl überschreitet, was jedoch zu Rundungsproblemen führen kann.
  3. Boolsche: JSON verwendet die Kleinbuchstaben true und false. MQL5 hingegen verwendet den Typ bool. Dies ist eine unkomplizierte Zuordnung, aber wir müssen diese Token (true und false) beim Parsen sorgfältig erkennen. Es gibt einen kleinen Haken: Syntaxfehler – wie True oder FALSE mit Großbuchstaben – sind kein gültiges JSON, obwohl einige Parser in anderen Sprachen sie zulassen. Wenn Ihre Daten manchmal Boolesche Großbuchstaben verwenden, müssen Sie dies differenziert handhaben oder sicherstellen, dass Ihre Daten streng JSON-konform sind.
  4. NULL: Ein Nullwert in JSON weist oft auf ein leeres oder fehlendes Feld hin. MQL5 hat keinen eigenen „Typ Null“. Stattdessen können wir JSON null als eine spezielle interne Enumeration darstellen (wie jtNULL, wenn wir eine Enumeration für unsere JSON-Elementtypen definieren) oder es als leere Zeichenkette oder Standardwert behandeln. Wir werden bald sehen, wie man Nullen im Parser verwaltet.
  5. Objekte: Wenn Sie geschweifte Klammern sehen, { ... } sehen, ist das ein JSON-Objekt. Es handelt sich im Wesentlichen um eine Sammlung von Schlüssel-Wert-Paaren. In MQL5 gibt es keinen integrierten Wörterbuchtyp, aber wir können einen simulieren, indem wir ein dynamisches Array von Paaren speichern oder eine nutzerdefinierte Klasse erstellen, die Schlüssel und Werte enthält. Normalerweise definieren wir so etwas wie eine CMyJsonObject-Klasse (oder eine allgemeine Klasse mit einem internen Status „Objekt“), die eine Liste von Kindern enthält. Jedes Kind hat einen Schlüssel (String) und einen Wert, der ein beliebiger JSON-Datentyp sein kann.
  6. Arrays: Arrays in JSON sind geordnete Listen, umgeben von eckigen Klammern, [ ... ]. Jedes Element in einem Array kann eine Zeichenkette, eine Zahl, ein Objekt oder sogar ein anderes Array sein. In MQL5 behandeln wir Arrays mit der Funktion ArrayResize und direkter Indizierung. Wir werden wahrscheinlich ein JSON-Array als dynamisches Array von Elementen speichern. Unser Code muss die Tatsache, dass ein bestimmter Knoten ein Array ist, zusammen mit den darin enthaltenen Kindern verfolgen.

Sehen wir uns einige der potenziellen Herausforderungen an:

  1. Umgang mit Escape-Sequenzen: In JSON kann ein Backslash \ vor Zeichen wie Anführungszeichen oder Zeilenumbrüchen stehen. Zum Beispiel könnten Sie sehen "description": "Line one\\nLine two" . Wir müssen \\n als einen tatsächlichen Zeilenumbruch innerhalb der endgültigen Zeichenfolge interpretieren. Besondere Sequenzen sind:
    • \" für doppelte Anführungszeichen
    • \\ für Backslash
    • \/ manchmal für Schrägstrich
    • \n für Zeilenumbruch
    • \t für tab
    • \u für Unicode-Codepunkte

    Wir müssen diese Sequenzen im rohen JSON-String methodisch in die tatsächlichen Zeichen umwandeln, die sie in MQL5 darstellen. Andernfalls speichert der Parser sie möglicherweise falsch oder schlägt bei Eingabedaten, die diese Standard-Escape-Muster verwenden, fehl.

  2. Leerzeichen und Steuerzeichen trimmen: Eine gültige JSON-Zeichenkette kann Leerzeichen, Tabulatoren und Zeilenumbrüche (insbesondere zwischen Elementen) enthalten. Obwohl diese erlaubt sind und an den meisten Stellen keine semantische Bedeutung haben, können sie die Parsing-Logik verkomplizieren, wenn wir nicht vorsichtig sind. Ein robuster Parser ignoriert in der Regel alle Leerzeichen außerhalb von Anführungsstrichen. Das bedeutet, dass wir sie auslassen wollen, wenn wir von einem Token zum nächsten gehen.
  3. Der Umgang mit großen Daten: Wenn Ihre JSON-Zeichenfolge extrem groß ist, könnten Sie sich Sorgen über Speicherbeschränkungen in MQL5 machen. Die Sprache kann mit Arrays recht gut umgehen, aber es gibt Obergrenzen, wenn man sich zig Millionen Elementen nähert. Die meisten Händler benötigen nur selten JSON in diesem Umfang, aber es ist erwähnenswert, dass ein „Streaming“- oder iterativer Ansatz notwendig sein kann, wenn Sie dies tun. Für die meisten normalen Verwendungszwecke, wie z. B. Leseeinstellungen oder mäßig große Datensätze, sollte unser einfacher Ansatz völlig ausreichen.
  4. Nicht jedes JSON ist perfekt. Wenn Ihr Parser versucht, eine ungültige Struktur zu lesen – z. B. ein fehlendes Anführungszeichen oder ein nachgestelltes Komma – muss er diese differenziert behandeln. Möglicherweise möchten Sie Fehlercodes definieren oder eine Fehlermeldung intern speichern, damit der aufrufende Code Parse-Fehler erkennen und darauf reagieren kann. In einem Handelskontext könnten Sie das:

    • Zeigen Sie ein Meldungsfenster an oder drucken Sie eine Fehlermeldung in das Journal.
    • Zurück zu einigen sicheren Einstellungen, wenn JSON ungültig ist.
    • Stoppen Sie die Ausführung des Expert Advisors, wenn kritische Daten nicht geparst werden können.

Wir werden grundlegende Prüfungen einbauen, um Fehler wie nicht übereinstimmende Klammern oder nicht erkannte Token zu erkennen. Auch eine erweiterte Fehlerberichterstattung ist möglich, aber das hängt davon ab, wie gründlich Sie sein wollen.

Da JSON verschachtelt sein kann, wird unser Parser wahrscheinlich eine einzelne Klasse oder Klassenhierarchie verwenden, bei der jeder Knoten einer von mehreren Typen sein kann:

  • Object – Enthält Schlüssel-Wert-Paare
  • Array – Enthält indizierte Elemente
  • String – Enthält Textdaten
  • Number – Speichert numerische Daten als Double oder möglicherweise als Long
  • Boolean – Wahr oder falsch
  • Null – Kein Wert

Wir könnten eine Enumeration für diese möglichen Knotentypen implementieren, z. B.:

enum JSONNodeType
  {
   JSON_UNDEFINED = 0,
   JSON_OBJECT,
   JSON_ARRAY,
   JSON_STRING,
   JSON_NUMBER,
   JSON_BOOL,
   JSON_NULL
  };
enum JSONNodeType
  {
   JSON_UNDEFINED = 0,
   JSON_OBJECT,
   JSON_ARRAY,
   JSON_STRING,
   JSON_NUMBER,
   JSON_BOOL,
   JSON_NULL
  };

Dann übergeben wir unserer Parserklasse eine Variable, die den Typ des aktuellen Knotens enthält. Wir speichern auch den Inhalt des Knotens. Wenn es sich um ein Objekt handelt, behalten wir ein Array von Kindknoten, die durch einen String verschlüsselt sind. Wenn es sich um ein Array handelt, wird eine Liste von Kindknoten geführt, die von 0 aufwärts indiziert sind. Wenn es eine Zeichenkette ist, bleibt es eine Zeichenkette. Wenn es sich um eine Zahl handelt, könnte ein Double plus eine interne Ganzzahl gespeichert werden, wenn es sich um eine ganze Zahl handelt, usw.

Ein alternativer Ansatz besteht darin, eine eigene Klasse für Objekte, Arrays, Strings usw. zu haben. Das kann in MQL5 unübersichtlich werden, weil man oft zwischen ihnen hin- und herwechseln muss. Stattdessen werden wir wahrscheinlich eine einzige Klasse (oder eine einzige Hauptklasse plus einige Hilfsstrukturen) verwenden, die dynamisch jeden JSON-Typ darstellen kann. Dieser einheitliche Ansatz ist bei verschachtelten Elementen einfach, da jedes untergeordnete Element im Wesentlichen der gleiche Knotentyp mit einem anderen internen Typ ist. Das hilft uns, den Code kürzer und allgemeiner zu halten.

Selbst wenn Ihr unmittelbares Projekt nur das Lesen von JSON erfordert, möchten Sie vielleicht irgendwann JSON aus Ihren MQL5-Daten erstellen. Wenn Sie beispielsweise Handelssignale generieren und diese als JSON an einen Server weiterleiten möchten oder wenn Sie Ihre Handelsgeschäfte in einer strukturierten JSON-Datei protokollieren möchten, benötigen Sie einen „Encoder“ oder „Serializer“. Unser möglicher Parser kann dazu erweitert werden. Der grundlegende Code, den wir für den Umgang mit Strings und Arrays schreiben werden, kann auch bei der Erzeugung von JSON helfen. Behalten Sie das im Hinterkopf, wenn Sie Ihre Klassenmethoden entwerfen: „Wie kann ich dieselbe Logik in umgekehrter Reihenfolge aufrufen, um JSON-Text aus internen Daten zu erzeugen?“

Jetzt haben wir ein solides Verständnis dafür, wie die Strukturen von JSON mit MQL5 korrelieren. Wir wissen, dass wir eine flexible Klasse brauchen, die folgende Aufgaben erfüllen kann:

  1. Typ des Speicherknotens – Zahl, Zeichenfolge, Objekt, Array, Boolescher Wert oder Null.
  2. Parse – Liest den Rohtext, Zeichen für Zeichen, interpretiert geschweifte Klammern, Anführungszeichen und spezielle Token.
  3. Zugriff – Bietet bequeme Methoden an, um Kindknoten nach Schlüssel (für Objekte) oder nach Index (für Arrays) zu erhalten oder zu setzen.
  4. Konvertieren – Numerische oder boolesche Knoten in MQL5-Primitive, wie double, int oder bool umwandeln.
  5. Escape/Unescape – Konvertiert JSON-kodierte Sequenzen aus Zeichen in normale MQL5-Zeichenketten (und umgekehrt, wenn wir eine zukünftige Methode „to JSON“ hinzufügen).
  6. Fehlerprüfung – Möglicherweise werden fehlerhafte Eingaben oder unbekannte Token erkannt und dann differenziert behandelt.

Im nächsten Abschnitt, in dem die eigentliche Programmierarbeit beginnt, werden wir uns diese Funktionen Schritt für Schritt vornehmen. Wenn Sie sich Sorgen um die Leistung oder den Speicherbedarf machen, können Sie sicher sein, dass ein einfacher Ansatz für den normalen Gebrauch ausreichend schnell und speichereffizient ist. Wenn Sie auf Leistungsengpässe oder Speicherbeschränkungen stoßen, können Sie den Code jederzeit profilieren oder partielle Parsing-Techniken anwenden.

In Abschnitt 3 beginnen wir mit dem Aufbau unseres Parsers im Detail. Wir definieren die übergreifende Klasse – etwa CJsonNode – und beginnen mit den einfachsten Aufgaben: Speichern des Typs und des Werts eines Knotens sowie Schreiben der Methode „tokenizer“, die JSON-Token (wie geschweifte Klammern oder Anführungszeichen) identifiziert. Sobald das Fundament gelegt ist, werden wir uns nach oben arbeiten, um Objekte, Arrays, verschachtelte Elemente und Datenextraktion zu unterstützen.

Unabhängig davon, ob Sie kleine JSON-Konfigurationsdateien parsen oder umfangreiche Daten aus dem Internet abrufen möchten, gelten dieselben Grundlagen. Auch wenn Sie keine Erfahrung mit dem Lesen externer Daten in MQL5 haben, keine Sorge: Wenn Sie die Logik Schritt für Schritt nachvollziehen können, wird das Ganze recht überschaubar.

Atmen Sie jetzt durch, wir werden gleich in den Code eintauchen. Im nächsten Abschnitt werden wir den nutzerdefinierten JSON-Parser Schritt für Schritt aufbauen und praktische Tipps geben, um sicherzustellen, dass Ihre Daten zuverlässig verarbeitet werden. Bringen wir MQL5 dazu, JSON zu „sprechen“ wie ein Champion!

Die Kernklasse des Parsers: Das Ziel unserer Parserklasse ist es, ein beliebiges Stück JSON-Daten (manchmal als „node“ (Knoten) in einem Baum bezeichnet) darzustellen. Hier ist eine Skizze dessen, was wir brauchen könnten:

  1. Eine Enumeration der Knotentypen:  Wir wollen leicht zwischen JSON-Objekt, Array, String usw. unterscheiden. Definieren wir also:
    enum JsonNodeType
      {
       JSON_UNDEF  = 0,
       JSON_OBJ,
       JSON_ARRAY,
       JSON_STRING,
       JSON_NUMBER,
       JSON_BOOL,
       JSON_NULL
      };
    
  2. Mitgliedsvariablen: 
    Jeder CJsonNode speichert
    • Ein JsonNodeType m_typeto identifiziert den Knotentyp.
    • Für Objekte: eine Struktur (wie ein Array), die Schlüssel-Werte-Paare enthält.
    • Für Arrays: eine Struktur, die indizierte Unterknoten enthält.
    • Für Strings: eine Zeichenkette m_value.
    • Für Zahlen: ein double m_numVal, eventuell ein zusätzlicher long m_intVal, falls erforderlich.
    • Für Boolesche Werte: bool m_boolVal.
  3. Parsing- und Hilfsmethoden:
    • Eine Methode zum Parsen des rohen JSON-Textes.
    • Methoden zum Abrufen von Kindknoten nach Index oder Schlüssel.
    • Möglicherweise eine Methode zur „Tokenisierung“ der Eingabe, die uns hilft, Klammern, geschweifte Klammern, Zeichenketten, Boolesche Werte usw. zu erkennen.

Wir werden diese Ideen im Hinterkopf behalten, wenn wir mit der Codierung beginnen. Das folgende Beispiel zeigt, wie wir diese Klasse in MQL5 definieren könnten (in einer Datei mit dem Namen CJsonNode.mqh). Wir werden Schritt für Schritt vorgehen.

//+------------------------------------------------------------------+
//|      CJsonNode.mqh                                               |
//+------------------------------------------------------------------+
#pragma once

enum JsonNodeType
  {
   JSON_UNDEF  = 0,
   JSON_OBJ,
   JSON_ARRAY,
   JSON_STRING,
   JSON_NUMBER,
   JSON_BOOL,
   JSON_NULL
  };

// Class representing a single JSON node
class CJsonNode
  {
private:
   JsonNodeType     m_type;         // The type of this node
   string           m_value;        // Used if this node is a string
   double           m_numVal;       // Used if this node is a number
   bool             m_boolVal;      // Used if this node is a boolean

   // For arrays and objects, we'll keep child nodes in a dynamic array:
   CJsonNode        m_children[];   // The array for child nodes
   string           m_keys[];       // Only used if node is an object
                                     // For arrays, we’ll just rely on index

public:
   // Constructor & destructor
   CJsonNode();
   ~CJsonNode();

   // Parsing interface
   bool ParseString(const string jsonText);

   // Utility methods (we will define them soon)
   void  SetType(JsonNodeType nodeType);
   JsonNodeType GetType() const;
   int   ChildCount() const;

   // Accessing children
   CJsonNode* AddChild();
   CJsonNode* GetChild(int index);
   CJsonNode* GetChild(const string key);
   void        SetKey(int childIndex,const string key);

   // Setting and getting values
   void SetString(const string val);
   void SetNumber(const double val);
   void SetBool(bool val);
   void SetNull();

   string AsString() const;
   double AsNumber() const;
   bool   AsBool()   const;

   // We’ll add the actual parse logic in a dedicated private method
private:
   bool ParseRoot(string jsonText);
   bool ParseObject(string text, int &pos);
   bool ParseArray(string text, int &pos);
   bool ParseValue(string text, int &pos);
   bool SkipWhitespace(const string text, int &pos);
   // ... other helpers
  };

Im obigen Code:

  • m_children[]: Ein dynamisches Array, das mehrere untergeordnete CJsonNodeObjects speichern kann. Bei Arrays ist jedes Kind indiziert, während bei Objekten jedes Kind einen zugehörigen Schlüssel hat, der in m_keys[] gespeichert ist.
  • ParseString(const string jsonText): Diese öffentliche Methode ist unser „Haupteinstiegspunkt“. Sie füttern es mit einer JSON-Zeichenfolge, und es versucht, diese zu analysieren und die internen Daten des Knotens zu füllen.
  • ParseRoot, ParseObject, ParseArray, ParseValue: Wir werden jede dieser privaten Methoden definieren, um bestimmte JSON-Konstrukte zu behandeln.

Wir zeigen jetzt ein Skelett, aber wir werden gleich die Details ausarbeiten. Beim Parsen von JSON lesen wir von links nach rechts und ignorieren Leerzeichen, bis wir ein Strukturzeichen sehen. Zum Beispiel:

  • Ein „{“ bedeutet, dass ein Objekt beginnt.
  • Ein „[“ bedeutet, dass wir ein Array haben.
  • Ein „\“ bedeutet, dass eine Zeichenkette beginnen wird.
  • Eine Ziffer oder ein Minuszeichen kann eine Zahl bedeuten.
  • Die Sequenzen „true“, „false“ oder „null“ erscheinen auch in JSON.

Schauen wir uns eine vereinfachte Version davon an, wie wir einen ganzen Text in unserer ParseString-Methode analysieren können:

bool CJsonNode::ParseString(const string jsonText)
  {
   // Reset existing data first
   m_type   = JSON_UNDEF;
   m_value  = "";
   ArrayResize(m_children,0);
   ArrayResize(m_keys,0);

   int pos=0;
   return ParseRoot(jsonText) && SkipWhitespace(jsonText,pos) && pos>=StringLen(jsonText)-1;
  }
  • Reset – Wir löschen alle vorherigen Daten.
  • pos=0 – Dies ist unsere Zeichenposition in der Zeichenkette.
  • Aufruf von ParseRoot(jsonText) – Eine Funktion, die wir definieren werden, die m_type festlegt und m_children oder m_value nach Bedarf auffüllt.
  • SkipWhitespace(jsonText,pos) – Oft werden Leerzeichen, Tabulatoren oder Zeilenumbrüche übersprungen, die auftreten könnten.
  • Endposition prüfen – Wenn alles korrekt geparst wurde, sollte possh nahe am Ende der Zeichenkette sein. Andernfalls kann es zu Textrückständen oder einem Fehler kommen.

Schauen wir uns nun ParseRoot genauer an. Um es kurz zu machen, stellen Sie sich vor, dass es wie folgt aussieht:

bool CJsonNode::ParseRoot(string jsonText)
  {
   int pos=0;
   SkipWhitespace(jsonText,pos);

   // If it begins with '{', parse as object
   if(StringSubstr(jsonText,pos,1)=="{")
     {
      return ParseObject(jsonText,pos);
     }
   // If it begins with '[', parse as array
   if(StringSubstr(jsonText,pos,1)=="[")
     {
      return ParseArray(jsonText,pos);
     }

   // Otherwise, parse as a single value
   return ParseValue(jsonText,pos);
  }

Zur Veranschaulichung prüfen wir das erste Zeichen, das kein Leerzeichen ist, und entscheiden, ob es sich um ein Objekt „{“, ein Array „[“ oder etwas anderes handelt (z. B. eine Zeichenkette, eine Zahl, ein Boolescher Wert oder null). Unsere tatsächliche Implementierung kann defensiver sein und Fehler behandeln, wenn das Zeichen unerwartet ist.

Schauen wir uns an, wie wir verschiedene Fälle analysieren:

  1. Parsen eines Objekts: Wenn eine öffnende geschweifte Klammer „{“ zu sehen ist, wird ein Objektknoten erstellt. Wir suchen dann wiederholt nach Schlüssel-Wert-Paaren, bis wir auf eine schließende Klammer „}“ stoßen. Hier ist ein konzeptioneller Ausschnitt, wie ParseObjectmight funktionieren könnte:
    bool CJsonNode::ParseObject(string text, int &pos)
          {
           // We already know text[pos] == '{'
           m_type = JSON_OBJ;
           pos++; // move past '{'
           SkipWhitespace(text,pos);
        
           // If the next char is '}', it's an empty object
           if(StringSubstr(text,pos,1)=="}")
             {
              pos++;
              return true;
             }
        
           // Otherwise, parse key-value pairs in a loop
           while(true)
             {
              SkipWhitespace(text,pos);
              // The key must be a string in double quotes
              if(StringSubstr(text,pos,1)!="\"")
                return false; // or set an error
              // parse the string key (we’ll show a helper soon)
              string objKey = "";
              if(!ParseStringLiteral(text,pos,objKey))
                 return false;
        
              SkipWhitespace(text,pos);
              // Expect a colon
              if(StringSubstr(text,pos,1)!=":")
                 return false;
              pos++;
              
              // Now parse the value
              CJsonNode child;
              if(!child.ParseValue(text,pos))
                 return false;
        
              // Add the child to our arrays
              int newIndex = ArraySize(m_children);
              ArrayResize(m_children,newIndex+1);
              ArrayResize(m_keys,newIndex+1);
              m_children[newIndex] = child;
              m_keys[newIndex]     = objKey;
        
              SkipWhitespace(text,pos);
              // If next char is '}', object ends
              if(StringSubstr(text,pos,1)=="}")
                {
                 pos++;
                 return true;
                }
              // Otherwise, we expect a comma before the next pair
              if(StringSubstr(text,pos,1)!=",")
                 return false;
              pos++;
             }
           // unreachable
           return false;
          } 

    Erklärungen:

    • Wir bestätigen, dass das Zeichen { ist, setzen unseren Typ auf JSON_OBJ und erhöhen pos.
    • Folgt ein }, ist das Objekt leer.
    • Andernfalls wird eine Schleife durchlaufen, bis eine } oder ein Fehler angezeigt wird. Jede Iteration:
      • Analysiert einen String-Schlüssel in Anführungszeichen.
      • Überspringt Leerzeichen und warten auf einen Doppelpunkt „:“.
      • Analysiert den nächsten Wert (der eine Zeichenkette, eine Zahl, ein Array, ein Objekt usw. sein kann).
      • Speichert diese in unseren Arrays (m_children und m_keys).
      • Wenn wir „}“ sehen, sind wir fertig. Wenn wir ein Komma sehen, fahren wir fort.

    Diese Schleife ist zentral für das Lesen eines JSON-Objekts. Die Struktur wird für Arrays wiederholt, außer dass Arrays keine Schlüssel haben, sondern nur indizierte Elemente.

  2. Parsen eines Arrays: Arrays beginnen mit „[“. Darin befinden sich null oder mehr durch Kommata getrennte Elemente. Etwa so:

    [ "Hello", 123, false, {"nestedObj": 1}, [10, 20] ]
    

    Code:

    bool CJsonNode::ParseArray(string text, int &pos)
      {
       m_type = JSON_ARRAY;
       pos++; // skip '['
       SkipWhitespace(text,pos);
    
       // If it's immediately ']', it's an empty array
       if(StringSubstr(text,pos,1)=="]")
         {
          pos++;
          return true;
         }
    
       // Otherwise, parse elements in a loop
       while(true)
         {
          SkipWhitespace(text,pos);
          CJsonNode child;
          if(!child.ParseValue(text,pos))
             return false;
    
          // store the child
          int newIndex = ArraySize(m_children);
          ArrayResize(m_children,newIndex+1);
          m_children[newIndex] = child;
    
          SkipWhitespace(text,pos);
          // if next char is ']', array ends
          if(StringSubstr(text,pos,1)=="]")
            {
             pos++;
             return true;
            }
          // must find a comma otherwise
          if(StringSubstr(text,pos,1)!=",")
             return false;
          pos++;
         }
    
       return false;
      }
    

    Wir überspringen „[“ und alle Leerzeichen. Wenn wir ] sehen, ist es leer. Andernfalls werden die Elemente in einer Schleife analysiert, bis wir ] erreichen. Der Hauptunterschied zu Objekten besteht darin, dass wir keine Schlüssel-Wert-Paare analysieren, sondern nur Werte, und zwar nacheinander.

  3. Parsen eines Wertes, Werte in JSON können eine Zeichenfolge, eine Zahl, ein Objekt, ein Array, ein Boolescher Wert oder null sein. Unser ParseValuemight tun könnte etwas wie:

    bool CJsonNode::ParseValue(string text, int &pos)
      {
       SkipWhitespace(text,pos);
       string c = StringSubstr(text,pos,1);
    
       // Object
       if(c=="{")
         {
          return ParseObject(text,pos);
         }
       // Array
       if(c=="[")
         {
          return ParseArray(text,pos);
         }
       // String
       if(c=="\"")
         {
          m_type = JSON_STRING;
          return ParseStringLiteral(text,pos,m_value);
         }
       // Boolean or null
       // We’ll look for 'true', 'false', or 'null'
       if(StringSubstr(text,pos,4)=="true")
         {
          m_type    = JSON_BOOL;
          m_boolVal = true;
          pos+=4;
          return true;
         }
       if(StringSubstr(text,pos,5)=="false")
         {
          m_type    = JSON_BOOL;
          m_boolVal = false;
          pos+=5;
          return true;
         }
       if(StringSubstr(text,pos,4)=="null")
         {
          m_type = JSON_NULL;
          pos+=4;
          return true;
         }
    
       // Otherwise, treat it as a number or fail
       return ParseNumber(text,pos);
      }
    

    Hier :

    1. überspringen wir die Leerzeichen.
    2. Wir schauen uns das aktuelle Zeichen (oder die Teilzeichenkette) an, um zu sehen, ob es {, [, " etc. ist.
    3. Rufen Sie die entsprechende Parse-Funktion auf.
    4. Wenn wir „wahr“, „falsch“ oder „null“ finden, behandeln wir sie direkt.
    5. Wenn nichts anderes übereinstimmt, gehen wir davon aus, dass es sich um eine Nummer handelt.

    Je nach Bedarf können Sie eine bessere Fehlerbehandlung hinzufügen. Wenn die Teilzeichenkette zum Beispiel nicht mit einem erkannten Muster übereinstimmt, können Sie einen Fehler setzen.

  4. Parsen einer Zahl; Wir müssen etwas analysieren, das numerisch aussieht, wie 123, 3.14 oder -0.001. Wir können einen schnellen Ansatz implementieren, indem wir so lange scannen, bis wir ein nicht-numerisches Zeichen erreichen:

    bool CJsonNode::ParseNumber(string text, int &pos)
      {
       m_type = JSON_NUMBER;
    
       // capture starting point
       int startPos = pos;
       while(pos < StringLen(text))
         {
          string c = StringSubstr(text,pos,1);
          if(c=="-" || c=="+" || c=="." || c=="e" || c=="E" || (c>="0" && c<="9"))
            {
             pos++;
            }
          else break;
         }
    
       // substring from startPos to pos
       string numStr = StringSubstr(text,startPos,pos-startPos);
       if(StringLen(numStr)==0)
         return false;
    
       // convert to double
       m_numVal = StringToDouble(numStr);
       return true;
      }
    

    Erlaubt sind Ziffern, ein optionales Vorzeichen ( – oder +), Dezimalpunkte und die Exponentenschreibweise (e oder E). Sobald wir auf etwas anderes stoßen – ein Leerzeichen, ein Komma oder eine Klammer – hören wir auf. Dann wandeln wir die Teilzeichenkette in den Typ double. Wenn Ihr Code zwischen ganzen Zahlen und Dezimalzahlen unterscheiden muss, können Sie zusätzliche Prüfungen hinzufügen.


Erweiterung unseres Parsers um erweiterte Funktionen

Inzwischen haben wir einen funktionalen JSON-Parser in MQL5, der Objekte, Arrays, Strings, Zahlen, Boolesche Werte und Nullwerte verarbeiten kann. In diesem Abschnitt werden wir uns mit weiteren Funktionen und Verbesserungen befassen. Wir werden erörtern, wie man untergeordnete Elemente auf bequemere Weise abruft, wie man potenzielle Fehler differenziert behandelt und sogar wie man Daten wieder in JSON-Text konvertiert. Wenn Sie diese Verbesserungen auf den von uns entwickelten Parser aufsetzen, erhalten Sie ein robusteres und flexibleres Werkzeug, das eine Vielzahl von realen Anforderungen erfüllen kann.
  1. Abrufen von Kindern nach Schlüssel oder Index

    Wenn unser Parser wirklich nützlich sein soll, wollen wir den Wert eines bestimmten Schlüssels in einem Objekt oder den Wert bei einem bestimmten Index in einem Array leicht abrufen können. Ein Beispiel: Wir haben dieses JSON:

    {
      "symbol": "EURUSD",
      "lots": 0.02,
      "settings": {
        "slippage": 2,
        "retries": 3
      }
    }

    Nehmen wir an, wir haben es in ein CJsonNode-Stammobjekt namens rootNode geparst. Wir würden gerne Dinge tun wie:

    string sym = rootNode.GetChild("symbol").AsString();
    double lot = rootNode.GetChild("lots").AsNumber();
    int slip   = rootNode.GetChild("settings").GetChild("slippage").AsNumber();

    Unsere derzeitige Codestruktur könnte dies ermöglichen, wenn wir GetChild(const string key) im Parser definieren. Hier sehen Sie, wie eine solche Methode in Ihrer CJsonNodeclass aussehen könnte:

    CJsonNode* CJsonNode::GetChild(const string key)
      {
       if(m_type != JSON_OBJ) 
          return NULL;
    
       // We look through m_keys to find a match
       for(int i=0; i<ArraySize(m_keys); i++)
         {
          if(m_keys[i] == key)
             return &m_children[i];
         }
       return NULL;
      }
    

    Wenn der aktuelle Knoten kein Objekt ist, geben wir auf diese Weise einfach NULL zurück. Andernfalls werden alle m_keyst durchsucht, um einen passenden zu finden. Ist dies der Fall, geben wir einen Zeiger auf das entsprechende Kind zurück.

    Ebenso können wir eine Methode für Arrays definieren:

    CJsonNode* CJsonNode::GetChild(int index)
      {
       if(m_type != JSON_ARRAY) 
          return NULL;
    
       if(index < 0 || index >= ArraySize(m_children))
          return NULL;
    
       return &m_children[index];
      }

    Handelt es sich bei dem Knoten um ein Array, werden einfach die Grenzen geprüft und das entsprechende Element zurückgegeben. Wenn es sich nicht um ein Array handelt – oder der Index außerhalb des Bereichs liegt – wird NULL zurückgegeben. Die Prüfung auf NULL ist in Ihrem eigentlichen Code vor der Dereferenzierung entscheidend.

  2. Differenzierte Fehlerbehandlung

    In vielen realen Szenarien kann JSON missgebildet ankommen (z. B. fehlende Anführungszeichen, nachgestellte Kommas oder unerwartete Symbole). Ein robuster Parser sollte diese Fehler erkennen und melden. Sie können dies tun, indem Sie:

    1. Einen booleschen Wert zurückgeben: Die meisten unserer Parse-Methoden geben bereits bool zurück. Wenn etwas fehlschlägt, geben wir false zurück. Wir können aber auch eine interne Fehlermeldung wie m_errorMsg speichern, damit der aufrufende Code sehen kann, was schief gelaufen ist.

    2. Das Parsing fortsetzen oder abbrechen? Wenn Sie einen schwerwiegenden Parse-Fehler feststellen – z. B. ein unerwartetes Zeichen oder eine nicht geschlossene Klammer – können Sie den gesamten Parse-Vorgang abbrechen und den Knoten in einem „ungültigen“ Zustand halten. Alternativ können Sie auch versuchen, etwas zu überspringen oder wiederherzustellen, aber das ist schon etwas fortgeschrittener.

    Hier ist eine konzeptionelle Verbesserung: innerhalb ParseArrayor ParseObject, wenn Sie etwas Unerwartetes sehen (wie ein Schlüssel ohne Anführungszeichen oder ein fehlender Doppelpunkt), können Sie schreiben:

    Print("Parse Error: Missing colon after key at position ", pos);
    return false;
    

    Dann könnten Sie in Ihrem Aufrufcode Folgendes tun:

    CJsonNode root;
        if(!root.ParseString(jsonText))
          {
           Print("Failed to parse JSON data. Check structure and try again.");
           // Perhaps handle defaults or stop execution
          }
    

    Es liegt an Ihnen, wie detailliert Sie diese Botschaften darstellen wollen. Manchmal reicht ein einziges „parse failed“ für ein Handelsszenario aus. In anderen Fällen benötigen Sie vielleicht mehr Nuancen, um Ihre JSON-Eingabe zu debuggen.

  3. MQL5-Daten zurück nach JSON konvertieren

    Das Lesen von JSON ist nur die halbe Miete. Was, wenn Sie Daten an einen Server zurücksenden oder Ihre eigenen Protokolle im JSON-Format schreiben müssen? Sie können Ihre CJsonNodeclass um eine „Serializer“-Methode erweitern, die die Daten des Knotens durchläuft und den JSON-Text rekonstruiert. Nennen wir sie zum Beispiel ToJsonString():

    string CJsonNode::ToJsonString() const
      {
       // We can define a helper that does the real recursion
       return SerializeNode(0);
      }
    
    string CJsonNode::SerializeNode(int depth) const
      {
       // If you prefer pretty-print with indentation, use 'depth'
       // For now, let's keep it simple:
       switch(m_type)
         {
          case JSON_OBJ:
             return SerializeObject(depth);
          case JSON_ARRAY:
             return SerializeArray(depth);
          case JSON_STRING:
             return "\""+EscapeString(m_value)+"\"";
          case JSON_NUMBER:
          {
             // Convert double to string carefully
             return DoubleToString(m_numVal, 10); 
          }
          case JSON_BOOL:
             return m_boolVal ? "true":"false";
          case JSON_NULL:
             return "null";
          default:
             return "\"\""; // or some placeholder
         }
      }
    

    Dann können Sie z.B. SerializeObject definieren:

    string CJsonNode::SerializeObject(int depth) const
      {
       string result = "{";
       for(int i=0; i<ArraySize(m_children); i++)
         {
          if(i>0) result += ",";
          string key   = EscapeString(m_keys[i]);
          string value = m_children[i].SerializeNode(depth+1);
          result += "\""+key+"\":";
          result += value;
         }
       result += "}";
       return result;
      }
    

    Ähnliches gilt für Arrays:

    string CJsonNode::SerializeArray(int depth) const
      {
       string result = "[";
       for(int i=0; i<ArraySize(m_children); i++)
         {
          if(i>0) result += ",";
          result += m_children[i].SerializeNode(depth+1);
         }
       result += "]";
       return result;
      }
    

    Sie werden feststellen, dass wir eine EscapeString-Funktion verwendet haben. Wir können den Code wiederverwenden, der JSON-String-Escapes behandelt, also Sonderzeichen in \“, \\, \n usw. umwandelt. Dadurch wird sichergestellt, dass die Ausgabe gültiges JSON ist, wenn sie Anführungszeichen oder Zeilenumbrüche enthält.

    Wenn Sie JSON „hübsch ausgedruckt“ haben möchten, fügen Sie einfach einige Zeilenumbrüche (“\n“) und Einrückungen ein. Ein Ansatz besteht darin, eine kurze Reihe von Leerzeichen auf der Grundlage der Tiefe zu erstellen, sodass Ihre JSON-Struktur optisch übersichtlicher wird:

    string indentation = "";
    for(int d=0; d<depth; d++)
       indentation += "  ";
    

    Dann fügen Sie diese Einrückung vor jeder Zeile oder jedem Element ein. Dies ist optional, aber praktisch, wenn Sie die JSON-Ausgabe regelmäßig manuell lesen oder debuggen müssen.

    Wenn Ihre JSON-Daten riesig sind, z. B. Zehntausende von Zeilen, müssen Sie möglicherweise die Leistung berücksichtigen:

    1. Effiziente String-Operationen
      Beachten Sie, dass wiederholte Teilstring-Operationen ( StringSubstr) aufwendig sein können. MQL5 ist ziemlich effizient, aber wenn Ihre Daten wirklich sehr umfangreich sind, sollten Sie das Parsing auf Basis von Chunks oder einen iterativen Ansatz in Betracht ziehen.

    2. Streaming vs. DOM-Parsing
      Unsere Strategie ist ein „DOM-ähnlicher“ Ansatz, d. h. wir zerlegen die gesamte Eingabe in eine Baumstruktur. Wenn die Daten so groß sind, dass sie nicht bequem in den Speicher passen, brauchen Sie einen Streaming-Parser, der ein Stück nach dem anderen verarbeitet. Das ist komplizierter, kann aber bei extrem großen Datensätzen notwendig sein.

    3. Caching
      Wenn Sie häufig dasselbe Objekt nach denselben Schlüsseln abfragen, können Sie diese in einer kleinen Map speichern oder direkte Zeiger beibehalten, um wiederholte Abfragen zu beschleunigen. Für typische Handelsaufgaben ist dies nur selten erforderlich, aber es ist eine Option, wenn die Leistung entscheidend ist.

  4. Bewährte Praktiken

    Im Folgenden finden Sie einige bewährte Verfahren, um Ihren Code sicher und wartbar zu halten:

    • Immer auf NULL prüfen
      Überprüfen Sie bei jedem Aufruf von GetChild(...), dass das Ergebnis nicht NULL ist. Der Versuch, auf einen Null-Zeiger in MQL5 zuzugreifen, kann zu Abstürzen oder merkwürdigem Verhalten führen.

    • Typen validieren
      Wenn Sie eine Zahl erwarten, das Kind aber in Wirklichkeit eine Zeichenkette ist, kann das ein Problem verursachen. Prüfen Sie beispielsweise GetType() oder verwenden Sie defensiven Code:

    CJsonNode* node = parent.GetChild("lots");
    if(node != NULL && node.GetType() == JSON_NUMBER)
      double myLots = node.AsNumber();
    

    So können Sie sicherstellen, dass Ihre Daten das sind, wofür Sie sie halten.

    Standardwerte

    Oft möchte man einen sicheren Fallback, wenn im JSON ein Schlüssel fehlt. Sie können eine Hilfsfunktion schreiben:

    double getDoubleOrDefault(CJsonNode &obj, const string key, double defaultVal)
      {
       CJsonNode* c = obj.GetChild(key);
       if(c == NULL || c.GetType() != JSON_NUMBER) 
         return defaultVal;
       return c.AsNumber();
      }
    

    Auf diese Weise kann Ihr Code fehlende oder ungültige Felder differenziert behandeln.

    • Achten Sie auf die Beschränkungen von Zeichenketten und Arrays von MQL5.
      MQL5 kann mit langen Zeichenketten umgehen, aber achten Sie auf den Speicherbedarf. Wenn Ihr JSON extrem groß ist, sollten Sie es sorgfältig testen.
      Auch die Größe von Arrays kann geändert werden, aber extrem große Arrays (Hunderttausende von Elementen) können unhandlich werden.

    • Tests
      Genauso wie Sie die Logik eines EAs mit historischen Daten testen würden, testen Sie Ihren JSON-Parser mit einer Vielzahl von Beispieleingaben:

      • Einfache Objekte
      • Verschachtelte Objekte
      • Arrays mit gemischten Daten
      • Große Zahlen, negative Zahlen
      • Boolescher Wert und Null
      • Zeichenketten mit Sonderzeichen oder Escape-Sequenzen

    Je mehr Varianten Sie ausprobieren, desto sicherer werden Sie sein, dass Ihr Parser robust ist.

An diesem Punkt haben wir unseren einfachen Parser in ein leistungsstarkes JSON-Dienstprogramm verwandelt. Wir können JSON-Strings in eine hierarchische Struktur parsen, Daten nach Schlüssel oder Index abrufen, Parse-Fehler behandeln und sogar Knoten wieder in JSON-Text serialisieren. Dies reicht für viele MQL5-Anwendungsfälle aus – wie das Lesen einer Konfigurationsdatei, das Abrufen von Daten aus dem Web (wenn Sie eine Brücke zu HTTP-Anfragen haben) oder das Erzeugen Ihrer eigenen JSON-Protokolle.

Im letzten Abschnitt werden wir ein vollständiges Code-Listing präsentieren, das alles, was wir besprochen haben, zusammenfasst. Sie können sie in Ihren MQL5-Editor als einzelne .mqh-Datei oder .mq5script einfügen, sie an Ihre Namenskonventionen anpassen und sofort mit JSON-Daten arbeiten. Neben dem endgültigen Code bieten wir abschließende Gedanken und einige Hinweise zur Erweiterung der Bibliothek, wenn Sie spezielle Anforderungen haben.


Vollständiger Code

Herzlichen Glückwunsch, dass Sie es so weit geschafft haben! Sie haben die Grundlagen von JSON in MQL5 kennengelernt, Schritt für Schritt einen Parser erstellt, ihn um erweiterte Funktionen erweitert und bewährte Verfahren für den praktischen Einsatz erkundet. Jetzt ist es an der Zeit, ein einziges, integriertes Code-Listing zu erstellen, das alle Fragmente zu einem kohärenten Modul zusammenfasst. Sie können diesen endgültigen Code in einer .mqhDatei (oder direkt in Ihrer .mq5Datei) platzieren und ihn überall dort einbinden, wo Sie JSON-Handling in Ihren MetaTrader 5-Projekten benötigen.

Nachfolgend finden Sie eine Beispielcode-Implementierung namens CJsonNode.mqh. Sie vereinheitlicht das Parsen von Objekten/Arrays, die Fehlerprüfung, die Serialisierung zurück zu JSON und das Abrufen nach Schlüssel oder Index.

Das ist wichtig: Dieser Code ist das Original und keine Kopie des zuvor bereitgestellten Referenzausschnitts. Sie folgt einer ähnlichen Parsing-Logik, unterscheidet sich aber von den anderen, um unseren Anforderungen an einen neuen Ansatz gerecht zu werden. Wie immer steht es Ihnen frei, Methodennamen anzupassen, eine robustere Fehlerbehandlung hinzuzufügen oder spezielle Funktionen nach Bedarf zu implementieren.

#ifndef __CJSONNODE_MQH__
#define __CJSONNODE_MQH__

//+------------------------------------------------------------------+
//| CJsonNode.mqh - A Minimalistic JSON Parser & Serializer in MQL5  |
//| Feel free to adapt as needed.                                    |
//+------------------------------------------------------------------+
#property strict

//--- Enumeration of possible JSON node types
enum JsonNodeType
  {
   JSON_UNDEF  = 0,
   JSON_OBJ,
   JSON_ARRAY,
   JSON_STRING,
   JSON_NUMBER,
   JSON_BOOL,
   JSON_NULL
  };

//+-----------------------------------------------------------------+
//| Class representing a single JSON node                           |
//+-----------------------------------------------------------------+
class CJsonNode
  {
public:
   //--- Constructor & Destructor
   CJsonNode();
   ~CJsonNode();

   //--- Parse entire JSON text
   bool        ParseString(string jsonText);

   //--- Check if node is valid
   bool        IsValid();

   //--- Get potential error message if not valid
   string      GetErrorMsg();

   //--- Access node type
   JsonNodeType GetType();

   //--- For arrays
   int         ChildCount();

   //--- For objects: get child by key
   CJsonNode*  GetChild(string key);

   //--- For arrays: get child by index
   CJsonNode*  GetChild(int index);

   //--- Convert to string / number / bool
   string      AsString();
   double      AsNumber();
   bool        AsBool();

   //--- Serialize back to JSON
   string      ToJsonString();

private:
   //--- Data members
   JsonNodeType m_type;       // Type of this node (object, array, etc.)
   string       m_value;      // For storing string content if node is string
   double       m_numVal;     // For numeric values
   bool         m_boolVal;    // For boolean values
   CJsonNode    m_children[]; // Child nodes (for objects and arrays)
   string       m_keys[];     // Keys for child nodes (valid if JSON_OBJ)
   bool         m_valid;      // True if node is validly parsed
   string       m_errMsg;     // Optional error message for debugging

   //--- Internal methods
   void         Reset();
   bool         ParseValue(string text,int &pos);
   bool         ParseObject(string text,int &pos);
   bool         ParseArray(string text,int &pos);
   bool         ParseNumber(string text,int &pos);
   bool         ParseStringLiteral(string text,int &pos);
   bool         ParseKeyLiteral(string text,int &pos,string &keyOut);
   string       UnescapeString(string input_);
   bool         SkipWhitespace(string text,int &pos);
   bool         AllWhitespace(string text,int pos);
   string       SerializeNode();
   string       SerializeObject();
   string       SerializeArray();
   string       EscapeString(string s);
};

//+-----------------------------------------------------------------+
//| Constructor                                                     |
//+-----------------------------------------------------------------+
CJsonNode::CJsonNode()
  {
   m_type    = JSON_UNDEF;
   m_value   = "";
   m_numVal  = 0.0;
   m_boolVal = false;
   m_valid   = true;
   ArrayResize(m_children,0);
   ArrayResize(m_keys,0);
   m_errMsg  = "";
  }

//+-----------------------------------------------------------------+
//| Destructor                                                      |
//+-----------------------------------------------------------------+
CJsonNode::~CJsonNode()
  {
   // No dynamic pointers to free; arrays are handled by MQL itself
  }

//+-----------------------------------------------------------------+
//| Parse entire JSON text                                          |
//+-----------------------------------------------------------------+
bool CJsonNode::ParseString(string jsonText)
  {
   Reset();
   int pos = 0;
   bool res = (ParseValue(jsonText,pos) && SkipWhitespace(jsonText,pos));

   // If there's leftover text that's not whitespace, it's an error
   if(pos < StringLen(jsonText))
     {
      if(!AllWhitespace(jsonText,pos))
        {
         m_valid  = false;
         m_errMsg = "Extra data after JSON parsing.";
         res      = false;
        }
     }
   return (res && m_valid);
  }

//+-----------------------------------------------------------------+
//| Check if node is valid                                          |
//+-----------------------------------------------------------------+
bool CJsonNode::IsValid()
  {
   return m_valid;
  }

//+-----------------------------------------------------------------+
//| Get potential error message if not valid                        |
//+-----------------------------------------------------------------+
string CJsonNode::GetErrorMsg()
  {
   return m_errMsg;
  }

//+-----------------------------------------------------------------+
//| Access node type                                                |
//+-----------------------------------------------------------------+
JsonNodeType CJsonNode::GetType()
  {
   return m_type;
  }

//+------------------------------------------------------------------+
//| For arrays: get number of children                               |
//+------------------------------------------------------------------+
int CJsonNode::ChildCount()
  {
   return ArraySize(m_children);
  }

//+------------------------------------------------------------------+
//| For objects: get child by key                                    |
//+------------------------------------------------------------------+
CJsonNode* CJsonNode::GetChild(string key)
  {
   if(m_type != JSON_OBJ)
      return NULL;
   for(int i=0; i<ArraySize(m_keys); i++)
     {
      if(m_keys[i] == key)
         return &m_children[i];
     }
   return NULL;
  }

//+------------------------------------------------------------------+
//| For arrays: get child by index                                   |
//+------------------------------------------------------------------+
CJsonNode* CJsonNode::GetChild(int index)
  {
   if(m_type != JSON_ARRAY)
      return NULL;
   if(index<0 || index>=ArraySize(m_children))
      return NULL;
   return &m_children[index];
  }

//+------------------------------------------------------------------+
//| Convert to string / number / bool                                |
//+------------------------------------------------------------------+
string CJsonNode::AsString()
  {
   if(m_type == JSON_STRING) return m_value;
   if(m_type == JSON_NUMBER) return DoubleToString(m_numVal,8);
   if(m_type == JSON_BOOL)   return m_boolVal ? "true" : "false";
   if(m_type == JSON_NULL)   return "null";
   // For object/array/undefined, return empty or handle as needed
   return "";
  }

//+------------------------------------------------------------------+
//| Convert node to numeric                                          |
//+------------------------------------------------------------------+
double CJsonNode::AsNumber()
  {
   if(m_type == JSON_NUMBER) return m_numVal;
   // If bool, return 1 or 0
   if(m_type == JSON_BOOL)   return (m_boolVal ? 1.0 : 0.0);
   return 0.0;
  }

//+------------------------------------------------------------------+
//| Convert node to boolean                                          |
//+------------------------------------------------------------------+
bool CJsonNode::AsBool()
  {
   if(m_type == JSON_BOOL)   return m_boolVal;
   if(m_type == JSON_NUMBER) return (m_numVal != 0.0);
   if(m_type == JSON_STRING) return (StringLen(m_value) > 0);
   return false;
  }

//+------------------------------------------------------------------+
//| Serialize node back to JSON                                      |
//+------------------------------------------------------------------+
string CJsonNode::ToJsonString()
  {
   return SerializeNode();
  }

//+------------------------------------------------------------------+
//| Reset node to initial state                                      |
//+------------------------------------------------------------------+
void CJsonNode::Reset()
  {
   m_type    = JSON_UNDEF;
   m_value   = "";
   m_numVal  = 0.0;
   m_boolVal = false;
   m_valid   = true;
   ArrayResize(m_children,0);
   ArrayResize(m_keys,0);
   m_errMsg  = "";
  }

//+------------------------------------------------------------------+
//| Dispatch parse based on first character                          |
//+------------------------------------------------------------------+
bool CJsonNode::ParseValue(string text,int &pos)
  {
   if(!SkipWhitespace(text,pos)) return false;
   if(pos >= StringLen(text))    return false;

   string c = StringSubstr(text,pos,1);

   //--- Object
   if(c == "{")
      return ParseObject(text,pos);

   //--- Array
   if(c == "[")
      return ParseArray(text,pos);

   //--- String
   if(c == "\"")
      return ParseStringLiteral(text,pos);

   //--- Boolean / null
   if(StringSubstr(text,pos,4) == "true")
     {
      m_type    = JSON_BOOL;
      m_boolVal = true;
      pos      += 4;
      return true;
     }
   if(StringSubstr(text,pos,5) == "false")
     {
      m_type    = JSON_BOOL;
      m_boolVal = false;
      pos      += 5;
      return true;
     }
   if(StringSubstr(text,pos,4) == "null")
     {
      m_type = JSON_NULL;
      pos   += 4;
      return true;
     }

   //--- Otherwise, parse number
   return ParseNumber(text,pos);
  }

//+------------------------------------------------------------------+
//| Parse object: { ... }                                            |
//+------------------------------------------------------------------+
bool CJsonNode::ParseObject(string text,int &pos)
  {
   m_type = JSON_OBJ;
   pos++; // skip '{'
   if(!SkipWhitespace(text,pos)) return false;

   //--- Check for empty object
   if(pos < StringLen(text) && StringSubstr(text,pos,1) == "}")
     {
      pos++;
      return true;
     }

   //--- Parse key-value pairs
   while(pos < StringLen(text))
     {
      if(!SkipWhitespace(text,pos)) return false;

      // Expect key in quotes
      if(pos >= StringLen(text) || StringSubstr(text,pos,1) != "\"")
        {
         m_valid  = false;
         m_errMsg = "Object key must start with double quote.";
         return false;
        }

      string key = "";
      if(!ParseKeyLiteral(text,pos,key))
         return false;

      if(!SkipWhitespace(text,pos)) return false;
      // Expect a colon
      if(pos >= StringLen(text) || StringSubstr(text,pos,1) != ":")
        {
         m_valid  = false;
         m_errMsg = "Missing colon after object key.";
         return false;
        }
      pos++; // skip ':'
      if(!SkipWhitespace(text,pos)) return false;

      // Parse the child value
      CJsonNode child;
      if(!child.ParseValue(text,pos))
        {
         m_valid  = false;
         m_errMsg = "Failed to parse object value.";
         return false;
        }

      // Store
      int idx = ArraySize(m_children);
      ArrayResize(m_children,idx+1);
      ArrayResize(m_keys,idx+1);
      m_children[idx] = child;
      m_keys[idx]     = key;

      if(!SkipWhitespace(text,pos)) return false;
      if(pos >= StringLen(text))    return false;

      string nextC = StringSubstr(text,pos,1);
      if(nextC == "}")
        {
         pos++;
         return true;
        }
      if(nextC != ",")
        {
         m_valid  = false;
         m_errMsg = "Missing comma in object.";
         return false;
        }
      pos++; // skip comma
     }

   return false; // didn't see closing '}'
  }

//+------------------------------------------------------------------+
//| Parse array: [ ... ]                                             |
//+------------------------------------------------------------------+
bool CJsonNode::ParseArray(string text,int &pos)
  {
   m_type = JSON_ARRAY;
   pos++; // skip '['
   if(!SkipWhitespace(text,pos)) return false;

   //--- Check for empty array
   if(pos < StringLen(text) && StringSubstr(text,pos,1) == "]")
     {
      pos++;
      return true;
     }

   //--- Parse elements
   while(pos < StringLen(text))
     {
      CJsonNode child;
      if(!child.ParseValue(text,pos))
        {
         m_valid  = false;
         m_errMsg = "Failed to parse array element.";
         return false;
        }
      int idx = ArraySize(m_children);
      ArrayResize(m_children,idx+1);
      m_children[idx] = child;

      if(!SkipWhitespace(text,pos)) return false;
      if(pos >= StringLen(text))    return false;

      string nextC = StringSubstr(text,pos,1);
      if(nextC == "]")
        {
         pos++;
         return true;
        }
      if(nextC != ",")
        {
         m_valid  = false;
         m_errMsg = "Missing comma in array.";
         return false;
        }
      pos++; // skip comma
      if(!SkipWhitespace(text,pos)) return false;
     }

   return false; // didn't see closing ']'
  }

//+------------------------------------------------------------------+
//| Parse a numeric value                                            |
//+------------------------------------------------------------------+
bool CJsonNode::ParseNumber(string text,int &pos)
  {
   m_type = JSON_NUMBER;
   int startPos = pos;

   // Scan allowed chars in a JSON number
   while(pos < StringLen(text))
     {
      string c = StringSubstr(text,pos,1);
      if(c=="-" || c=="+" || c=="." || c=="e" || c=="E" || (c>="0" && c<="9"))
        pos++;
      else
        break;
     }

   string numStr = StringSubstr(text,startPos,pos - startPos);
   if(StringLen(numStr) == 0)
     {
      m_valid  = false;
      m_errMsg = "Expected number, found empty.";
      return false;
     }

   m_numVal = StringToDouble(numStr);
   return true;
  }

//+------------------------------------------------------------------+
//| Parse a string literal (leading quote already checked)           |
//+------------------------------------------------------------------+
bool CJsonNode::ParseStringLiteral(string text,int &pos)
  {
   pos++;  // skip leading quote
   string result = "";

   while(pos < StringLen(text))
     {
      string c = StringSubstr(text,pos,1);
      if(c == "\"")
        {
         // closing quote
         pos++;
         m_type  = JSON_STRING;
         m_value = UnescapeString(result);
         return true;
        }
      if(c == "\\")
        {
         // handle escape
         pos++;
         if(pos >= StringLen(text))
            break;
         string ec = StringSubstr(text,pos,1);
         result += ("\\" + ec); // accumulate, we'll decode later
         pos++;
        }
      else
        {
         result += c;
         pos++;
        }
     }

   // If we get here, string was not closed
   m_valid  = false;
   m_errMsg = "Unclosed string literal.";
   return false;
  }

//+------------------------------------------------------------------+
//| Parse a string key (similar to a literal)                        |
//+------------------------------------------------------------------+
bool CJsonNode::ParseKeyLiteral(string text,int &pos,string &keyOut)
  {
   pos++;  // skip leading quote
   string buffer = "";

   while(pos < StringLen(text))
     {
      string c = StringSubstr(text,pos,1);
      if(c == "\"")
        {
         pos++;
         keyOut = UnescapeString(buffer);
         return true;
        }
      if(c == "\\")
        {
         pos++;
         if(pos >= StringLen(text))
            break;
         string ec = StringSubstr(text,pos,1);
         buffer += ("\\" + ec);
         pos++;
        }
      else
        {
         buffer += c;
         pos++;
        }
     }

   m_valid  = false;
   m_errMsg = "Unclosed key string.";
   return false;
  }

//+------------------------------------------------------------------+
//| Unescape sequences like \" \\ \n etc.                            |
//+------------------------------------------------------------------+
string CJsonNode::UnescapeString(string input_)
  {
   string out = "";
   int i      = 0;

   while(i < StringLen(input_))
     {
      string c = StringSubstr(input_,i,1);
      if(c == "\\")
        {
         i++;
         if(i >= StringLen(input_))
           {
            // Single backslash at end
            out += "\\";
            break;
           }
         string ec = StringSubstr(input_,i,1);

         if(ec == "\"")      out += "\"";
         else if(ec == "\\") out += "\\";
         else if(ec == "n")  out += "\n";
         else if(ec == "r")  out += "\r";
         else if(ec == "t")  out += "\t";
         else if(ec == "b")  out += CharToString(8);   // ASCII backspace
         else if(ec == "f")  out += CharToString(12);  // ASCII formfeed
         else                out += ("\\" + ec);

         i++;
        }
      else
        {
         out += c;
         i++;
        }
     }
   return out;
  }

//+------------------------------------------------------------------+
//| Skip whitespace                                                  |
//+------------------------------------------------------------------+
bool CJsonNode::SkipWhitespace(string text,int &pos)
  {
   while(pos < StringLen(text))
     {
      ushort c = StringGetCharacter(text,pos);
      if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
        pos++;
      else
        break;
     }
   // Return true if we haven't gone beyond string length
   return (pos <= StringLen(text));
  }

//+------------------------------------------------------------------+
//| Check if remainder is all whitespace                             |
//+------------------------------------------------------------------+
bool CJsonNode::AllWhitespace(string text,int pos)
  {
   while(pos < StringLen(text))
     {
      ushort c = StringGetCharacter(text,pos);
      if(c != ' ' && c != '\t' && c != '\n' && c != '\r')
         return false;
      pos++;
     }
   return true;
  }

//+------------------------------------------------------------------+
//| Serialization dispatcher                                         |
//+------------------------------------------------------------------+
string CJsonNode::SerializeNode()
  {
   switch(m_type)
     {
      case JSON_OBJ:    return SerializeObject();
      case JSON_ARRAY:  return SerializeArray();
      case JSON_STRING: return "\""+EscapeString(m_value)+"\"";
      case JSON_NUMBER: return DoubleToString(m_numVal,8);
      case JSON_BOOL:   return (m_boolVal ? "true" : "false");
      case JSON_NULL:   return "null";
      default:          return "\"\""; // undefined => empty string
     }
  }

//+------------------------------------------------------------------+
//| Serialize object                                                 |
//+------------------------------------------------------------------+
string CJsonNode::SerializeObject()
  {
   string out = "{";
   for(int i=0; i<ArraySize(m_children); i++)
     {
      if(i > 0) out += ",";
      out += "\""+EscapeString(m_keys[i])+"\":";
      out += m_children[i].SerializeNode();
     }
   out += "}";
   return out;
  }

//+------------------------------------------------------------------+
//| Serialize array                                                  |
//+------------------------------------------------------------------+
string CJsonNode::SerializeArray()
  {
   string out = "[";
   for(int i=0; i<ArraySize(m_children); i++)
     {
      if(i > 0) out += ",";
      out += m_children[i].SerializeNode();
     }
   out += "]";
   return out;
  }

//+------------------------------------------------------------------+
//| Escape a string for JSON output (backslashes, quotes, etc.)      |
//+------------------------------------------------------------------+
string CJsonNode::EscapeString(string s)
  {
   string out = "";
   for(int i=0; i<StringLen(s); i++)
     {
      ushort c = StringGetCharacter(s,i);
      switch(c)
        {
         case 34:  // '"'
            out += "\\\"";
            break;
         case 92:  // '\\'
            out += "\\\\";
            break;
         case 10:  // '\n'
            out += "\\n";
            break;
         case 13:  // '\r'
            out += "\\r";
            break;
         case 9:   // '\t'
            out += "\\t";
            break;
         case 8:   // backspace
            out += "\\b";
            break;
         case 12:  // formfeed
            out += "\\f";
            break;
         default:
            // Directly append character
            out += CharToString(c);
            break;
        }
     }
   return out;
  }

#endif // __CJSONNODE_MQH__

Nehmen wir ein Beispiel für seine Verwendung in einem Skript:

//+------------------------------------------------------------------+
//|                                                      ProjectName |
//|                                      Copyright 2020, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
#property strict
#include <CJsonNode.mqh>

void OnStart()
  {
   // Some JSON text
   string jsonText = "{\"name\":\"Alice\",\"age\":30,\"admin\":true,\"items\":[1,2,3],\"misc\":null}";

   CJsonNode parser;
   if(parser.ParseString(jsonText))
     {
      Print("JSON parsed successfully!");
      Print("Name: ", parser.GetChild("name").AsString());
      Print("Age: ",  parser.GetChild("age").AsNumber());
      Print("Admin?",  parser.GetChild("admin").AsBool());
      // Serialize back
      Print("Re-serialized JSON: ", parser.ToJsonString());
     }
   else
     {
      Print("JSON parsing error: ", parser.GetErrorMsg());
     }
  }

//+------------------------------------------------------------------+

Die erwartete Ausgabe ist selbsterklärend. Probieren Sie es einfach aus.


Schlussfolgerung

Mit diesem endgültigen Code in der Hand haben Sie alles, was Sie brauchen, um JSON direkt in MetaTrader 5 zu analysieren, zu manipulieren und sogar zu generieren:

  • Parsen von JSON: ParseString() wandelt Rohtext in eine strukturierte Knotenhierarchie um.
  • Daten abfragen: Mit GetChild(key) und GetChild(index) können Sie einfach durch Objekte und Arrays navigieren.
  • Validierung: Prüfungen von IsValid() und GetErrorMsg() stellen fest, ob das Parsing erfolgreich war oder ob es Probleme gab (z. B. falsch gesetzte Klammern).
  • Serialisierung: ToJsonString() setzt den Knoten (und die Kinder) wieder in gültigen JSON-Text zusammen.

Sie können diese Bibliothek gerne an Ihre speziellen Bedürfnisse anpassen. So können Sie beispielsweise umfassendere Fehlerberichte, spezielle numerische Konvertierungen oder Streaming-Funktionen für sehr große Datensätze hinzufügen. Die Grundlage sollte jedoch für die meisten typischen Anwendungsfälle, wie das Lesen von Parametern aus einer Datei oder die Interaktion mit webbasierten APIs, ausreichend sein.

Das war's! Sie haben das Ende unseres tiefen Einblicks in die JSON-Verarbeitung in MQL5 erreicht. Ganz gleich, ob Sie eine komplexe, datengesteuerte Handels-Engine implementieren oder nur Konfigurationsparameter aus einer lokalen Datei laden, ein zuverlässiger JSON-Parser und -Serialisierer kann Ihnen das Leben sehr erleichtern. Wir hoffen, dass dieser Artikel (und der darin enthaltene Code) Ihnen hilft, JSON reibungslos in Ihre automatisierten Handelsabläufe zu integrieren.

Viel Spaß beim Coding! Viel Spaß beim Handeln!

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16791

Beigefügte Dateien |
CJsonNode.mqh (41.39 KB)
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 11): Heikin Ashi Signal EA Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 11): Heikin Ashi Signal EA
MQL5 bietet unendlich viele Möglichkeiten, automatisierte Handelssysteme zu entwickeln, die auf Ihre Wünsche zugeschnitten sind. Wussten Sie, dass er sogar komplexe mathematische Berechnungen durchführen kann? In diesem Artikel stellen wir die japanische Heikin Ashi Technik als automatisierte Handelsstrategie vor.
Automatisieren von Handelsstrategien in MQL5 (Teil 5): Die Entwicklung der Strategie „Adaptive Crossover RSI Trading Suite“ Automatisieren von Handelsstrategien in MQL5 (Teil 5): Die Entwicklung der Strategie „Adaptive Crossover RSI Trading Suite“
In diesem Artikel entwickeln wir ein System für die Strategie „Adaptive Crossover RSI Trading Suite“, das das Kreuzen der gleitende Durchschnitte mit Periodenlängen von 14 und 50 als Signale verwendet, die durch einen 14-periodischen RSI-Filter bestätigt werden. Das System umfasst einen Filter für den Handelstag, Signalpfeile mit Kommentaren und ein Echtzeit-Dashboard zur Überwachung. Dieser Ansatz gewährleistet Präzision und Anpassungsfähigkeit beim automatisierten Handel.
Feature Engineering mit Python und MQL5 (Teil II): Winkel des Preises (2), Polarkoordinaten Feature Engineering mit Python und MQL5 (Teil II): Winkel des Preises (2), Polarkoordinaten
In diesem Artikel unternehmen wir den zweiten Versuch, die Veränderungen des Preisniveaus auf einem beliebigen Markt in eine entsprechende Veränderung des Winkels umzuwandeln. Diesmal haben wir einen mathematisch anspruchsvolleren Ansatz gewählt als bei unserem ersten Versuch, und die Ergebnisse, die wir erhalten haben, legen nahe, dass unsere Änderung des Ansatzes die richtige Entscheidung war. Diskutieren Sie heute mit uns, wie wir Polarkoordinaten verwenden können, um den Winkel zu berechnen, der durch Veränderungen der Preisniveaus gebildet wird, und zwar auf sinnvolle Weise, unabhängig davon, welchen Markt Sie gerade analysieren.
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 10): External Flow (II) VWAP Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 10): External Flow (II) VWAP
Meistern Sie die Macht des VWAP mit unserem umfassenden Leitfaden! Lernen Sie, wie Sie mit MQL5 und Python die VWAP-Analyse in Ihre Handelsstrategie integrieren können. Optimieren Sie Ihre Markteinblicke und verbessern Sie Ihre Handelsentscheidungen noch heute.