//+------------------------------------------------------------------+//+------------------------------------------------------------------+ //| JsonLib.mqh | //| Version 10.0 | //| Copyright 2025, ding9736 | //+------------------------------------------------------------------+ #property strict #property copyright "Copyright 2025, ding9736" #property link "https://www.mql5.com/en/users/ding9736" #property link "https://github.com/ding9736/Mql5-JsonLib" #property version "10.0" #ifndef MQL5_JSON_LIBRARY_V10_H #define MQL5_JSON_LIBRARY_V10_H /* //+------------------------------------------------------------------+ //| MQL5 JSON Library - Developer's Manual | //| Author: ding9736 | //| Version: 10.0 | //+------------------------------------------------------------------+ //============================================================================== // 1. Overview //============================================================================== // // The MQL5 JSON Library is a powerful, feature-rich library designed specifically // for parsing, manipulating, and serializing JSON data within the MQL5 // environment. It provides a simple and intuitive Document Object Model (DOM) API, // aiming to make the JSON handling experience in MQL5 comparable to modern // programming languages like JavaScript and Python. // // This library is capable of handling a wide range of tasks, from reading simple // EA configurations to complex real-time data exchange between systems. // Its main functions and features include: // // --- Parsing and Creation --- // // - **Load from String or File**: Reliably parses JSON text into manipulable // in-memory objects (`JsonParse`, `JsonFromFile`). // - **Build from Scratch**: Easily create new JSON objects and arrays // programmatically using concise APIs like `JsonNewObject` and `JsonNewArray`. // - **Flexible Parser**: Optionally supports some non-standard features of JSON5, // such as code comments and trailing commas, to enhance compatibility with // various data sources. // // --- Manipulation and Access --- // // - **Intuitive DOM Traversal**: Access data using intuitive syntax with keys // (`node["key"]`) and indices (`node[0]`), just like using a Python dictionary // or a JavaScript object. // - **Safe Type Conversion**: Provides a series of methods with default values, // such as `AsInt(defaultValue)` and `AsString(defaultValue)`, allowing you // to safely extract data of the desired type from a node without worrying // about program crashes due to type mismatches or non-existent paths. // - **Dynamic Modification**: Freely add, update, or delete key-value pairs // in JSON objects and elements in arrays (`Set`, `Add`, `Remove`). // // --- Advanced Querying and Processing --- // // - **Powerful Query Engine**: Built-in support for **JSON Pointer** (RFC 6901, // for direct path access) and **JSONPath** (for complex and fuzzy queries), // enabling efficient extraction of one or more data nodes from deeply // nested, complex structures, either in bulk or with precision. // - **Low-Memory Stream Parsing**: Provides `JsonStreamParser` for processing // gigabyte-scale, huge JSON files. It reads the file token by token in an // event stream manner without loading the entire file into memory, thus // achieving ultimate memory efficiency. // - **Utility Functions**: Offers advanced features like document cloning (`.Clone()`) // and deep merging (`JsonMerge`), which greatly simplify common complex tasks // such as merging "default configuration" with "user configuration". // // --- Robustness and Safety --- // // - **Automatic Memory Management**: Adopts the RAII (Resource Acquisition Is // Initialization) design pattern. `JsonDocument` is responsible for managing // the lifecycle of all its nodes. Developers do not need to manually `new`/`delete` // any JSON elements, fundamentally eliminating the risk of memory leaks. // - **Cross-Document Operation Safety**: When assigning a node between different // `JsonDocument` instances, the library automatically performs a safe deep // copy (Clone), preventing dangling pointers and accidental data corruption. // - **Detailed Error Reporting**: When parsing fails, the `JsonError` object // provides detailed information including the error line number, column number, // and context, facilitating rapid problem diagnosis. // //============================================================================== // 2. Core Concepts & Memory Management //============================================================================== // // [!!] 2.1 Namespace - The Key to Integrating Your Project [!!] // **Most Important Tip**: All classes in this library (e.g., `JsonDocument`, // `JsonNode`) and global functions (e.g., `JsonParse`) are encapsulated // within a namespace called `MQL5_Json`. // // **How to Use It Correctly:** // // - **In Header Files (.mqh)**: MQL5 does not allow `using namespace` in the // global scope of header files. **Therefore, you must use fully qualified names**. // This is the only reliable way in multi-file projects. // Incorrect: `JsonDocument doc;` // Correct: `MQL5_Json::JsonDocument doc;` // // - **Inside Functions in Main Program Files (.mq5)**: For convenience, you can use // `using namespace MQL5_Json;` inside functions, but to ensure the // generality of the examples, all code in this manual will use the fully // qualified name approach. // // **If you encounter the `'JsonNode' - declaration without type` compilation error,** // **it is almost always because you forgot to add the `MQL5_Json::` prefix** // **to the types and functions.** // // [!!] 2.2 Memory Management Model // `JsonDocument` **owns** the data; `JsonNode` is just a **view**. // // [!!] 2.3 Object Passing in MQL5 // MQL5 requires that all class objects (including `JsonNode`) passed as function // arguments **must be passed by reference (using &)**. // Incorrect: `void myFunction(MQL5_Json::JsonNode node)` // Correct: `void myFunction(MQL5_Json::JsonNode &node)` // //============================================================================== // 3. API Usage & Examples //============================================================================== //--- 3.1. Quick Start ----------------------------------------------- // // void OnStart() // { // string jsonText = "{ \"name\": \"John Doe\", \"age\": 30, \"isStudent\": false, \"courses\": [\"MQL5\", \"C++\"] }"; // // // 1. Parse JSON string (using fully qualified names) // MQL5_Json::JsonError error; // MQL5_Json::JsonParseOptions options; // MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(jsonText, error, options); // // // Always check if the document is valid after parsing // if (!doc.IsValid()) // { // Print("Failed to parse JSON: ", error.ToString()); // return; // } // // // 2. Access data // MQL5_Json::JsonNode root = doc.GetRoot(); // string name = root.Get("name").AsString("Unknown"); // long age = root.Get("age").AsInt(0); // bool isStudent = root["isStudent"].AsBool(true); // Using the [] operator // // PrintFormat("Name: %s, Age: %d, Is Student: %s", name, age, isStudent ? "Yes" : "No"); // // // 3. Create a new JSON document // MQL5_Json::JsonDocument newDoc = MQL5_Json::JsonNewObject(); // newDoc.GetRoot().Set("status", "OK"); // newDoc.GetRoot().Set("code", 200); // // // 4. Serialize the new JSON (pretty format) // Print("Newly created JSON:\n", newDoc.ToString(true)); // } // //--- 3.2. Parsing JSON ---------------------------------------------- // // void ParseFromStringAndFile() // { // string json_with_comments = "{ \"key\": \"value\", / * comment * / \"key2\": 123, } // trailing comma"; // // MQL5_Json::JsonError error; // MQL5_Json::JsonParseOptions options; // options.allow_trailing_commas = true; // options.allow_comments = true; // // MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(json_with_comments, error, options); // if (doc.IsValid()) { Print("Flexible parsing successful! Result: ", doc.ToString()); } // // MQL5_Json::JsonDocument docFromFile = MQL5_Json::JsonFromFile("config.json", error, options); // if (docFromFile.IsValid()) { Print("Successfully loaded configuration from file."); } // } // //--- 3.3. Creating JSON ----------------------------------------------- // // void CreateJson() // { // MQL5_Json::JsonDocument doc = MQL5_Json::JsonNewObject(); // MQL5_Json::JsonNode root = doc.GetRoot(); // // root.Set("product_id", 12345); // root.Set("available", true); // // MQL5_Json::JsonNode specs = doc.CreateObjectNode(); // specs.Set("color", "black"); // root.Set("specifications", specs); // // MQL5_Json::JsonNode tags = doc.CreateArrayNode(); // tags.Add("electronics"); // root.Set("tags", tags); // // Print("Created JSON:\n", doc.ToString(true)); // } // //--- 3.4. Accessing & Querying Data -------------------------- // // void AccessData() // { // string jsonText = "{ \"data\": { \"user_id\": 101, \"tags\": [\"active\"] } }"; // MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(jsonText, {}, {}); // if(!doc.IsValid()) return; // // MQL5_Json::JsonNode root = doc.GetRoot(); // // long user_id = root["data"]["user_id"].AsInt(-1); // string first_tag = root.Get("data").Get("tags").At(0).AsString("default"); // // PrintFormat("User ID: %d, First Tag: %s", user_id, first_tag); // } // // void QueryWithJsonPathAndPointer() // { // string text = "{ \"store\": { \"book\": [ { \"title\": \"MQL5 Basics\" } ] } }"; // MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(text, {}, {}); // if(!doc.IsValid()) return; // // string title = doc.GetRoot().Query("/store/book/0/title").AsString(); // Print("JSON Pointer Result: ", title); // // MQL5_Json::JsonNode nodes[]; // MQL5_Json::JsonError error; // int count = doc.GetRoot().SelectNodes(nodes, "$.store.book[*].title", error); // if(count > 0) { Print("JSONPath Result: ", nodes[0].AsString()); } // } // //--- 3.5. Modifying Data ---------------------------------------------- // // void ModifyJson() // { // MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse("{ \"a\": 1, \"b\": 2 }", {}, {}); // MQL5_Json::JsonNode root = doc.GetRoot(); // root.Set("a", 100); // root.Remove("b"); // Print("Modified JSON:\n", doc.ToString(true)); // } // //--- 3.6. Serialization ------------------------------------------------ // // void SerializeJson() // { // MQL5_Json::JsonDocument doc = MQL5_Json::JsonNewObject(); // doc.GetRoot().Set("message", "hello world"); // // string pretty_str = doc.ToString(true); // Print("Pretty Format:\n", pretty_str); // // doc.SaveToFile("output.json", true); // } // //============================================================================== // 4. Advanced Features //============================================================================== //--- 4.1. Stream Parsing Large Files (Low Memory Usage) --- // // class CTradeCounter : public MQL5_Json::IJsonStreamHandler // { // private: // int m_count; // public: // int GetCount() const { return m_count; } // bool OnStartDocument() override { m_count=0; return true; } // bool OnString(const string &value) override // { // if(value == "EURUSD") m_count++; // return true; // } // // (Other empty methods omitted for brevity) // }; // // void TestStreamParser() // { // string big_json = "[{\"s\":\"EURUSD\"},{\"s\":\"EURUSD\"}]"; // MQL5_Json::JsonStreamParser parser; // CTradeCounter *handler = new CTradeCounter(); // MQL5_Json::JsonError error; // // if(parser.Parse(big_json, handler, error, {})) // Print("Stream parser found count: ", handler.GetCount()); // delete handler; // } // //--- 4.2. Merging Documents (JsonMerge) --- // // void TestJsonMerge() // { // MQL5_Json::JsonDocument target = MQL5_Json::JsonParse("{ \"a\": 1 }", {}, {}); // MQL5_Json::JsonDocument patch = MQL5_Json::JsonParse("{ \"b\": 2 }", {}, {}); // MQL5_Json::JsonDocument result = MQL5_Json::JsonMerge(target, patch); // Print("Merge result:\n", result.ToString(true)); // } // //--- 4.3. Cloning Documents (Clone) --- // // void TestClone() // { // MQL5_Json::JsonDocument base = MQL5_Json::JsonNewObject(); // base.GetRoot().Set("magic", 12345); // // MQL5_Json::JsonDocument backtest = base.Clone(); // backtest.GetRoot().Set("magic", 67890); // // Print("Cloned and modified config:\n", backtest.ToString(true)); // } // //--- 4.4. Iteration using the Visitor Pattern (ForEach) --- // // class CSumVisitor : public MQL5_Json::IJsonArrayVisitor // { // public: // double sum; // void Visit(int index, const MQL5_Json::JsonNode &item) override // { // if(item.IsNumber()) sum += item.AsDouble(); // } // }; // // void TestForEach() // { // MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse("[10, 20.5, 30]", {}, {}); // CSumVisitor visitor; // doc.GetRoot().ForEach(GetPointer(visitor)); // Print("Array sum: ", visitor.sum); // } // //--- 4.5. JSONPath - Selecting a Single Node (SelectSingleNode) --- // // void TestSelectSingleNode() // { // string text = "{ \"store\": { \"bicycle\": { \"color\": \"red\"} } }"; // MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(text, {}, {}); // MQL5_Json::JsonError error; // MQL5_Json::JsonNode bike = doc.GetRoot().SelectSingleNode("$.store.bicycle", error); // if(bike.IsValid()) { Print("Found bicycle color: ", bike["color"].AsString()); } // } // //============================================================================== // 5. Design Philosophy & FAQ //============================================================================== // // --- Q: I have correctly #included JsonLib.mqh, so why am I still getting tons of // --- `'JsonNode' - declaration without type` compilation errors? // // **A: This is the most common integration issue and is 100% caused by improper namespace handling.** // // - **Diagnosis**: The compiler doesn't recognize `JsonNode` because it doesn't know to look inside the `MQL5_Json` namespace. // - **Solution**: In your code, modify all calls to `JsonLib` types and functions to their fully qualified form with the `MQL5_Json::` prefix. // // - **Example**: // // Incorrect code (will not compile in an .mqh file) // JsonDocument doc = JsonNewObject(); // // // Correct code (guaranteed to compile) // MQL5_Json::JsonDocument doc = MQL5_Json::JsonNewObject(); // // - **Root Cause**: MQL5 does not allow `using namespace` in the global scope of `.mqh` files. // // // --- Q: Why does the function parameter `JsonNode node` cause an `'objects are passed by reference only'` error? // // **A: This is a mandatory rule of the MQL5 language.** // // - **The Rule**: MQL5 requires all class objects (including `JsonNode`) to be passed by reference (`&`). // - **Solution**: Always use `&` in your function signatures. // - **Example**: // // Incorrect // void ProcessNode(MQL5_Json::JsonNode node); // // // Correct // void ProcessNode(MQL5_Json::JsonNode &node); // //============================================================================== // 6. Best Practices & Important Notes //============================================================================== // // To ensure your project integrates smoothly and runs stably, please adhere to the following rules. // // 1. **Always Use Fully Qualified Names in `.mqh` Files (Crucial!)** // - This is the **number one rule** for successfully integrating `JsonLib` in multi-file projects. // - Always use `MQL5_Json::JsonDocument`, `MQL5_Json::JsonNode`, `MQL5_Json::JsonParse`, etc. // // 2. **Class Object Parameters Must Be Passed by Reference** // - When you write a function that needs to accept a `JsonNode` as a parameter, you must declare it as a reference (`&`). // - `void myFunction(MQL5_Json::JsonNode &node);` // Correct // - This is to comply with MQL5's language rules and will prevent the `'objects are passed by reference only'` compilation error. // // 3. **Memory and Object Lifecycle** // - `JsonDocument` **owns** the data; `JsonNode` is just a **view**. // - If a `JsonDocument` is destroyed, all of its `JsonNode`s become **invalid**. // // 4. **Robust Coding and Error Handling** // - After calling `JsonParse`, **always check `doc.IsValid()`**. // */ //+------------------------------------------------------------------+ #include "Core/JsonCore.mqh" #include "Core/JsonNode.mqh" #include "Core/JsonDocument.mqh" #include "Core/JsonStreamParser.mqh" #include "Core/JsonApiImpl.mqh" #endif // MQL5_JSON_LIBRARY_V10_H //+------------------------------------------------------------------+
//| MQL5 JSON Library - Developer's Manual |
//| Author: ding9736 |
//| Version: 10.0 |
//+------------------------------------------------------------------+
//==============================================================================
// 1. Overview
//==============================================================================
// The MQL5 JSON Library is a powerful, feature-rich library designed specifically
// for parsing, manipulating, and serializing JSON data within the MQL5
// environment. It provides a simple and intuitive Document Object Model (DOM) API,
// aiming to make the JSON handling experience in MQL5 comparable to modern
// programming languages like JavaScript and Python.
// This library is capable of handling a wide range of tasks, from reading simple
// EA configurations to complex real-time data exchange between systems.
// Its main functions and features include:
// --- Parsing and Creation ---
// - **Load from String or File**: Reliably parses JSON text into manipulable
// in-memory objects (`JsonParse`, `JsonFromFile`).
// - **Build from Scratch**: Easily create new JSON objects and arrays
// programmatically using concise APIs like `JsonNewObject` and `JsonNewArray`.
// - **Flexible Parser**: Optionally supports some non-standard features of JSON5,
// such as code comments and trailing commas, to enhance compatibility with
// various data sources.
// --- Manipulation and Access ---
// - **Intuitive DOM Traversal**: Access data using intuitive syntax with keys
// (`node["key"]`) and indices (`node[0]`), just like using a Python dictionary
// or a JavaScript object.
// - **Safe Type Conversion**: Provides a series of methods with default values,
// such as `AsInt(defaultValue)` and `AsString(defaultValue)`, allowing you
// to safely extract data of the desired type from a node without worrying
// about program crashes due to type mismatches or non-existent paths.
// - **Dynamic Modification**: Freely add, update, or delete key-value pairs
// in JSON objects and elements in arrays (`Set`, `Add`, `Remove`).
// --- Advanced Querying and Processing ---
// - **Powerful Query Engine**: Built-in support for **JSON Pointer** (RFC 6901,
// for direct path access) and **JSONPath** (for complex and fuzzy queries),
// enabling efficient extraction of one or more data nodes from deeply
// nested, complex structures, either in bulk or with precision.
// - **Low-Memory Stream Parsing**: Provides `JsonStreamParser` for processing
// gigabyte-scale, huge JSON files. It reads the file token by token in an
// event stream manner without loading the entire file into memory, thus
// achieving ultimate memory efficiency.
// - **Utility Functions**: Offers advanced features like document cloning (`.Clone()`)
// and deep merging (`JsonMerge`), which greatly simplify common complex tasks
// such as merging "default configuration" with "user configuration".
// --- Robustness and Safety ---
// - **Automatic Memory Management**: Adopts the RAII (Resource Acquisition Is
// Initialization) design pattern. `JsonDocument` is responsible for managing
// the lifecycle of all its nodes. Developers do not need to manually `new`/`delete`
// any JSON elements, fundamentally eliminating the risk of memory leaks.
// - **Cross-Document Operation Safety**: When assigning a node between different
// `JsonDocument` instances, the library automatically performs a safe deep
// copy (Clone), preventing dangling pointers and accidental data corruption.
// - **Detailed Error Reporting**: When parsing fails, the `JsonError` object
// provides detailed information including the error line number, column number,
// and context, facilitating rapid problem diagnosis.
//==============================================================================
// 2. Core Concepts & Memory Management
//==============================================================================
// [!!] 2.1 Namespace - The Key to Integrating Your Project [!!]
// **Most Important Tip**: All classes in this library (e.g., `JsonDocument`,
// `JsonNode`) and global functions (e.g., `JsonParse`) are encapsulated
// within a namespace called `MQL5_Json`.
// **How to Use It Correctly:**
// - **In Header Files (.mqh)**: MQL5 does not allow `using namespace` in the
// global scope of header files. **Therefore, you must use fully qualified names**.
// This is the only reliable way in multi-file projects.
// Incorrect: `JsonDocument doc;`
// Correct: `MQL5_Json::JsonDocument doc;`
// - **Inside Functions in Main Program Files (.mq5)**: For convenience, you can use
// `using namespace MQL5_Json;` inside functions, but to ensure the
// generality of the examples, all code in this manual will use the fully
// qualified name approach.
// **If you encounter the `'JsonNode' - declaration without type` compilation error,**
// **it is almost always because you forgot to add the `MQL5_Json::` prefix**
// **to the types and functions.**
// [!!] 2.2 Memory Management Model
// `JsonDocument` **owns** the data; `JsonNode` is just a **view**.
// [!!] 2.3 Object Passing in MQL5
// MQL5 requires that all class objects (including `JsonNode`) passed as function
// arguments **must be passed by reference (using &)**.
// Incorrect: `void myFunction(MQL5_Json::JsonNode node)`
// Correct: `void myFunction(MQL5_Json::JsonNode &node)`
//==============================================================================
// 3. API Usage & Examples
//==============================================================================
//--- 3.1. Quick Start -----------------------------------------------
// void OnStart()
// string jsonText = "{ \"name\": \"John Doe\", \"age\": 30, \"isStudent\": false, \"courses\": [\"MQL5\", \"C++\"] }";
// // 1. Parse JSON string (using fully qualified names)
// MQL5_Json::JsonError error;
// MQL5_Json::JsonParseOptions options;
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(jsonText, error, options);
// // Always check if the document is valid after parsing
// if (!doc.IsValid())
// {
// Print("Failed to parse JSON: ", error.ToString());
// return;
// }
// // 2. Access data
// MQL5_Json::JsonNode root = doc.GetRoot();
// string name = root.Get("name").AsString("Unknown");
// long age = root.Get("age").AsInt(0);
// bool isStudent = root["isStudent"].AsBool(true); // Using the [] operator
// PrintFormat("Name: %s, Age: %d, Is Student: %s", name, age, isStudent ? "Yes" : "No");
// // 3. Create a new JSON document
// MQL5_Json::JsonDocument newDoc = MQL5_Json::JsonNewObject();
// newDoc.GetRoot().Set("status", "OK");
// newDoc.GetRoot().Set("code", 200);
// // 4. Serialize the new JSON (pretty format)
// Print("Newly created JSON:\n", newDoc.ToString(true));
//--- 3.2. Parsing JSON ----------------------------------------------
// void ParseFromStringAndFile()
// string json_with_comments = "{ \"key\": \"value\", / * comment * / \"key2\": 123, } // trailing comma";
// MQL5_Json::JsonError error;
// MQL5_Json::JsonParseOptions options;
// options.allow_trailing_commas = true;
// options.allow_comments = true;
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(json_with_comments, error, options);
// if (doc.IsValid()) { Print("Flexible parsing successful! Result: ", doc.ToString()); }
// MQL5_Json::JsonDocument docFromFile = MQL5_Json::JsonFromFile("config.json", error, options);
// if (docFromFile.IsValid()) { Print("Successfully loaded configuration from file."); }
//--- 3.3. Creating JSON -----------------------------------------------
// void CreateJson()
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonNewObject();
// MQL5_Json::JsonNode root = doc.GetRoot();
// root.Set("product_id", 12345);
// root.Set("available", true);
// MQL5_Json::JsonNode specs = doc.CreateObjectNode();
// specs.Set("color", "black");
// root.Set("specifications", specs);
// MQL5_Json::JsonNode tags = doc.CreateArrayNode();
// tags.Add("electronics");
// root.Set("tags", tags);
// Print("Created JSON:\n", doc.ToString(true));
//--- 3.4. Accessing & Querying Data --------------------------
// void AccessData()
// string jsonText = "{ \"data\": { \"user_id\": 101, \"tags\": [\"active\"] } }";
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(jsonText, {}, {});
// if(!doc.IsValid()) return;
// MQL5_Json::JsonNode root = doc.GetRoot();
// long user_id = root["data"]["user_id"].AsInt(-1);
// string first_tag = root.Get("data").Get("tags").At(0).AsString("default");
// PrintFormat("User ID: %d, First Tag: %s", user_id, first_tag);
// void QueryWithJsonPathAndPointer()
// string text = "{ \"store\": { \"book\": [ { \"title\": \"MQL5 Basics\" } ] } }";
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(text, {}, {});
// if(!doc.IsValid()) return;
// string title = doc.GetRoot().Query("/store/book/0/title").AsString();
// Print("JSON Pointer Result: ", title);
// MQL5_Json::JsonNode nodes[];
// MQL5_Json::JsonError error;
// int count = doc.GetRoot().SelectNodes(nodes, "$.store.book[*].title", error);
// if(count > 0) { Print("JSONPath Result: ", nodes[0].AsString()); }
//--- 3.5. Modifying Data ----------------------------------------------
// void ModifyJson()
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse("{ \"a\": 1, \"b\": 2 }", {}, {});
// MQL5_Json::JsonNode root = doc.GetRoot();
// root.Set("a", 100);
// root.Remove("b");
// Print("Modified JSON:\n", doc.ToString(true));
//--- 3.6. Serialization ------------------------------------------------
// void SerializeJson()
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonNewObject();
// doc.GetRoot().Set("message", "hello world");
// string pretty_str = doc.ToString(true);
// Print("Pretty Format:\n", pretty_str);
// doc.SaveToFile("output.json", true);
//==============================================================================
// 4. Advanced Features
//==============================================================================
//--- 4.1. Stream Parsing Large Files (Low Memory Usage) ---
// class CTradeCounter : public MQL5_Json::IJsonStreamHandler
// private:
// int m_count;
// public:
// int GetCount() const { return m_count; }
// bool OnStartDocument() override { m_count=0; return true; }
// bool OnString(const string &value) override
// if(value == "EURUSD") m_count++;
// return true;
// // (Other empty methods omitted for brevity)
// void TestStreamParser()
// string big_json = "[{\"s\":\"EURUSD\"},{\"s\":\"EURUSD\"}]";
// MQL5_Json::JsonStreamParser parser;
// CTradeCounter *handler = new CTradeCounter();
// MQL5_Json::JsonError error;
// if(parser.Parse(big_json, handler, error, {}))
// Print("Stream parser found count: ", handler.GetCount());
// delete handler;
//--- 4.2. Merging Documents (JsonMerge) ---
// void TestJsonMerge()
// MQL5_Json::JsonDocument target = MQL5_Json::JsonParse("{ \"a\": 1 }", {}, {});
// MQL5_Json::JsonDocument patch = MQL5_Json::JsonParse("{ \"b\": 2 }", {}, {});
// MQL5_Json::JsonDocument result = MQL5_Json::JsonMerge(target, patch);
// Print("Merge result:\n", result.ToString(true));
//--- 4.3. Cloning Documents (Clone) ---
// void TestClone()
// MQL5_Json::JsonDocument base = MQL5_Json::JsonNewObject();
// base.GetRoot().Set("magic", 12345);
// MQL5_Json::JsonDocument backtest = base.Clone();
// backtest.GetRoot().Set("magic", 67890);
// Print("Cloned and modified config:\n", backtest.ToString(true));
//--- 4.4. Iteration using the Visitor Pattern (ForEach) ---
// class CSumVisitor : public MQL5_Json::IJsonArrayVisitor
// public:
// double sum;
// void Visit(int index, const MQL5_Json::JsonNode &item) override
// if(item.IsNumber()) sum += item.AsDouble();
// void TestForEach()
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse("[10, 20.5, 30]", {}, {});
// CSumVisitor visitor;
// doc.GetRoot().ForEach(GetPointer(visitor));
// Print("Array sum: ", visitor.sum);
//--- 4.5. JSONPath - Selecting a Single Node (SelectSingleNode) ---
// void TestSelectSingleNode()
// string text = "{ \"store\": { \"bicycle\": { \"color\": \"red\"} } }";
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonParse(text, {}, {});
// MQL5_Json::JsonError error;
// MQL5_Json::JsonNode bike = doc.GetRoot().SelectSingleNode("$.store.bicycle", error);
// if(bike.IsValid()) { Print("Found bicycle color: ", bike["color"].AsString()); }
//==============================================================================
// 5. Design Philosophy & FAQ
//==============================================================================
// --- Q: I have correctly #included JsonLib.mqh, so why am I still getting tons of
// --- `'JsonNode' - declaration without type` compilation errors?
// **A: This is the most common integration issue and is 100% caused by improper namespace handling.**
// - **Diagnosis**: The compiler doesn't recognize `JsonNode` because it doesn't know to look inside the `MQL5_Json` namespace.
// - **Solution**: In your code, modify all calls to `JsonLib` types and functions to their fully qualified form with the `MQL5_Json::` prefix.
// - **Example**:
// // Incorrect code (will not compile in an .mqh file)
// JsonDocument doc = JsonNewObject();
// // Correct code (guaranteed to compile)
// MQL5_Json::JsonDocument doc = MQL5_Json::JsonNewObject();
// - **Root Cause**: MQL5 does not allow `using namespace` in the global scope of `.mqh` files.
// --- Q: Why does the function parameter `JsonNode node` cause an `'objects are passed by reference only'` error?
// **A: This is a mandatory rule of the MQL5 language.**
// - **The Rule**: MQL5 requires all class objects (including `JsonNode`) to be passed by reference (`&`).
// - **Solution**: Always use `&` in your function signatures.
// - **Example**:
// // Incorrect
// void ProcessNode(MQL5_Json::JsonNode node);
// // Correct
// void ProcessNode(MQL5_Json::JsonNode &node);
//==============================================================================
// 6. Best Practices & Important Notes
//==============================================================================
// To ensure your project integrates smoothly and runs stably, please adhere to the following rules.
// 1. **Always Use Fully Qualified Names in `.mqh` Files (Crucial!)**
// - This is the **number one rule** for successfully integrating `JsonLib` in multi-file projects.
// - Always use `MQL5_Json::JsonDocument`, `MQL5_Json::JsonNode`, `MQL5_Json::JsonParse`, etc.
// 2. **Class Object Parameters Must Be Passed by Reference**
// - When you write a function that needs to accept a `JsonNode` as a parameter, you must declare it as a reference (`&`).
// - `void myFunction(MQL5_Json::JsonNode &node);` // Correct
// - This is to comply with MQL5's language rules and will prevent the `'objects are passed by reference only'` compilation error.
// 3. **Memory and Object Lifecycle**
// - `JsonDocument` **owns** the data; `JsonNode` is just a **view**.
// - If a `JsonDocument` is destroyed, all of its `JsonNode`s become **invalid**.
// 4. **Robust Coding and Error Handling**
// - After calling `JsonParse`, **always check `doc.IsValid()`**.