//+------------------------------------------------------------------+ //| AI JSON FILE.mqh | //| Copyright 2026, Allan Munene Mutiiria. | //| https://t.me/Forex_Algo_Trader | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" //--- Guard against multiple inclusion of this header #ifndef AI_JSON_FILE_MQH #define AI_JSON_FILE_MQH //--- Toggle verbose debug output during JSON parsing #define DEBUG_PRINT false //+------------------------------------------------------------------+ //| JSON value type enumeration | //+------------------------------------------------------------------+ enum JsonValueType { JsonUndefined, // Unset or unknown value JsonNull, // Explicit null literal JsonBoolean, // Boolean literal (true or false) JsonInteger, // Integer numeric literal JsonDouble, // Floating-point numeric literal JsonString, // String literal in quotes JsonArray, // Array of values JsonObject // Object of key-value pairs }; //+------------------------------------------------------------------+ //| JSON value class | //+------------------------------------------------------------------+ class JsonValue { public: //--- Hold child elements for arrays and objects JsonValue m_children[]; //--- Hold the key name when this value is inside an object string m_key; //--- Hold a temporary key while parsing object key-value pairs string m_temporaryKey; //--- Point to the parent value for hierarchy traversal JsonValue *m_parent; //--- Store the value type for this node JsonValueType m_type; //--- Store the boolean representation of the value bool m_booleanValue; //--- Store the integer representation of the value long m_integerValue; //--- Store the floating-point representation of the value double m_doubleValue; //--- Store the string representation of the value string m_stringValue; //--- Define the encoding code page used for character conversion static int encodingCodePage; //+---------------------------------------------------------------+ //| Default constructor | //+---------------------------------------------------------------+ JsonValue() { //--- Reset all members to defaults Reset(); } //+---------------------------------------------------------------+ //| Construct with parent and type | //+---------------------------------------------------------------+ JsonValue(JsonValue *parent, JsonValueType type) { //--- Reset all members to defaults Reset(); //--- Apply the requested type m_type = type; //--- Link to the supplied parent m_parent = parent; } //+---------------------------------------------------------------+ //| Construct from type and string value | //+---------------------------------------------------------------+ JsonValue(JsonValueType type, string value) { //--- Reset all members to defaults Reset(); //--- Populate the value from the string representation SetFromString(type, value); } //+---------------------------------------------------------------+ //| Construct from integer literal | //+---------------------------------------------------------------+ JsonValue(const int integerValue) { //--- Reset all members to defaults Reset(); //--- Mark the value as an integer m_type = JsonInteger; //--- Store the integer value m_integerValue = integerValue; //--- Mirror the value as a double m_doubleValue = (double)m_integerValue; //--- Mirror the value as a string m_stringValue = IntegerToString(m_integerValue); //--- Derive boolean from non-zero check m_booleanValue = m_integerValue != 0; } //+---------------------------------------------------------------+ //| Copy constructor | //+---------------------------------------------------------------+ JsonValue(const JsonValue &other) { //--- Reset all members to defaults Reset(); //--- Copy all data from the source value CopyFrom(other); } //+---------------------------------------------------------------+ //| Destructor | //+---------------------------------------------------------------+ ~JsonValue() { //--- Release members to defaults Reset(); } //+---------------------------------------------------------------+ //| Reset all members to default state | //+---------------------------------------------------------------+ void Reset() { //--- Clear parent link m_parent = NULL; //--- Clear stored key m_key = ""; //--- Clear temporary parsing key m_temporaryKey = ""; //--- Reset type to undefined m_type = JsonUndefined; //--- Reset boolean storage m_booleanValue = false; //--- Reset integer storage m_integerValue = 0; //--- Reset double storage m_doubleValue = 0; //--- Reset string storage m_stringValue = ""; //--- Empty the children array ArrayResize(m_children, 0); } //+---------------------------------------------------------------+ //| Copy key and data from source value | //+---------------------------------------------------------------+ bool CopyFrom(const JsonValue &source) { //--- Copy the key name m_key = source.m_key; //--- Copy the actual value data and children CopyDataFrom(source); //--- Indicate success return true; } //+---------------------------------------------------------------+ //| Copy value data and children from source | //+---------------------------------------------------------------+ void CopyDataFrom(const JsonValue &source) { //--- Copy the value type m_type = source.m_type; //--- Copy the boolean representation m_booleanValue = source.m_booleanValue; //--- Copy the integer representation m_integerValue = source.m_integerValue; //--- Copy the double representation m_doubleValue = source.m_doubleValue; //--- Copy the string representation m_stringValue = source.m_stringValue; //--- Copy nested children recursively CopyChildrenFrom(source); } //+---------------------------------------------------------------+ //| Copy children array and re-link parent pointers | //+---------------------------------------------------------------+ void CopyChildrenFrom(const JsonValue &source) { //--- Resize the children array to match the source int numChildren = ArrayResize(m_children, ArraySize(source.m_children)); //--- Iterate through each child slot for(int index = 0; index < numChildren; index++) { //--- Copy the child value m_children[index] = source.m_children[index]; //--- Re-link the child to point to this object as parent m_children[index].m_parent = GetPointer(this); } } //+---------------------------------------------------------------+ //| Find a child by its key name | //+---------------------------------------------------------------+ JsonValue *FindChildByKey(string key) { //--- Walk children in reverse to find the matching key for(int index = ArraySize(m_children) - 1; index >= 0; --index) { //--- Return pointer when key matches if(m_children[index].m_key == key) return GetPointer(m_children[index]); } //--- Indicate not found return NULL; } //+---------------------------------------------------------------+ //| Object index operator (string key) | //+---------------------------------------------------------------+ JsonValue *operator[](string key) { //--- Promote undefined node to object on first access if(m_type == JsonUndefined) m_type = JsonObject; //--- Try to find an existing child with the requested key JsonValue *value = FindChildByKey(key); //--- Return the existing child if found if(value) return value; //--- Build a new child placeholder bound to this parent JsonValue newValue(GetPointer(this), JsonUndefined); //--- Assign the requested key to the new child newValue.m_key = key; //--- Append the child internally and capture its pointer value = AddChildInternal(newValue); //--- Return the newly added child return value; } //+---------------------------------------------------------------+ //| Array index operator (integer index) | //+---------------------------------------------------------------+ JsonValue *operator[](int index) { //--- Promote undefined node to array on first access if(m_type == JsonUndefined) m_type = JsonArray; //--- Grow the array until the requested index exists while(index >= ArraySize(m_children)) { //--- Build a new placeholder child bound to this parent JsonValue newValue(GetPointer(this), JsonUndefined); //--- Append the child and bail out if pointer is invalid if(CheckPointer(AddChildInternal(newValue)) == POINTER_INVALID) return NULL; } //--- Return pointer to the requested child slot return GetPointer(m_children[index]); } //+---------------------------------------------------------------+ //| Assignment from another JsonValue | //+---------------------------------------------------------------+ void operator=(const JsonValue &value) { //--- Delegate to copy routine CopyFrom(value); } //+---------------------------------------------------------------+ //| Assignment from string literal | //+---------------------------------------------------------------+ void operator=(string stringValue) { //--- Choose string type when value is non-null, else null type m_type = (stringValue != NULL) ? JsonString : JsonNull; //--- Store the string representation m_stringValue = stringValue; //--- Derive an integer view of the string m_integerValue = StringToInteger(m_stringValue); //--- Derive a double view of the string m_doubleValue = StringToDouble(m_stringValue); //--- Derive boolean from non-null check m_booleanValue = stringValue != NULL; } //+---------------------------------------------------------------+ //| Assignment from integer literal | //+---------------------------------------------------------------+ void operator=(const int integerValue) { //--- Mark the type as integer m_type = JsonInteger; //--- Store the integer value m_integerValue = integerValue; //--- Mirror as double m_doubleValue = (double)m_integerValue; //--- Mirror as string m_stringValue = IntegerToString(m_integerValue); //--- Derive boolean from non-zero check m_booleanValue = integerValue != 0; } //+---------------------------------------------------------------+ //| Return the boolean representation | //+---------------------------------------------------------------+ bool ToBoolean() const { //--- Return cached boolean value return m_booleanValue; } //+---------------------------------------------------------------+ //| Return the string representation | //+---------------------------------------------------------------+ string ToString() { //--- Return cached string value return m_stringValue; } //+---------------------------------------------------------------+ //| Return the integer representation | //+---------------------------------------------------------------+ long ToInteger() const { //--- Return cached integer value return m_integerValue; } //+---------------------------------------------------------------+ //| Return the double representation | //+---------------------------------------------------------------+ double ToDouble() const { //--- Return cached double value return m_doubleValue; } //+---------------------------------------------------------------+ //| Populate value from a typed string source | //+---------------------------------------------------------------+ void SetFromString(JsonValueType type, string stringValue) { //--- Apply the requested type m_type = type; //--- Branch on the requested type switch(m_type) { case JsonBoolean: //--- Convert string to boolean via integer m_booleanValue = (StringToInteger(stringValue) != 0); //--- Mirror boolean as integer m_integerValue = (long)m_booleanValue; //--- Mirror boolean as double m_doubleValue = (double)m_booleanValue; //--- Cache the original string m_stringValue = stringValue; break; case JsonString: //--- Unescape any JSON escape sequences in the string m_stringValue = UnescapeString(stringValue); //--- Promote to null type when the string is null m_type = (m_stringValue != NULL) ? JsonString : JsonNull; //--- Derive integer view from the string m_integerValue = StringToInteger(m_stringValue); //--- Derive double view from the string m_doubleValue = StringToDouble(m_stringValue); //--- Derive boolean from non-null check m_booleanValue = m_stringValue != NULL; break; } } //+---------------------------------------------------------------+ //| Extract a substring from a character array | //+---------------------------------------------------------------+ string GetSubstringFromArray(char &jsonCharacterArray[], int startPosition, int substringLength) { //--- Return empty string when the requested length is invalid if(substringLength <= 0) return ""; //--- Build a temporary character buffer char temporaryArray[]; //--- Copy the requested range from the source array ArrayCopy(temporaryArray, jsonCharacterArray, 0, startPosition, substringLength); //--- Convert the buffer to a string using the active code page return CharArrayToString(temporaryArray, 0, WHOLE_ARRAY, encodingCodePage); } //+---------------------------------------------------------------+ //| Add a child JsonValue | //+---------------------------------------------------------------+ JsonValue *AddChild(const JsonValue &item) { //--- Promote undefined node to array on first child added if(m_type == JsonUndefined) m_type = JsonArray; //--- Delegate to internal append routine return AddChildInternal(item); } //+---------------------------------------------------------------+ //| Add a child from a string value | //+---------------------------------------------------------------+ JsonValue *AddChild(string stringValue) { //--- Build a string-typed JsonValue from the input JsonValue item(JsonString, stringValue); //--- Append it as a child return AddChild(item); } //+---------------------------------------------------------------+ //| Add a child from an integer value | //+---------------------------------------------------------------+ JsonValue *AddChild(const int integerValue) { //--- Build an integer-typed JsonValue from the input JsonValue item(integerValue); //--- Append it as a child return AddChild(item); } //+---------------------------------------------------------------+ //| Internal append helper that grows the children array | //+---------------------------------------------------------------+ JsonValue *AddChildInternal(const JsonValue &item) { //--- Capture the current size as the insertion index int currentSize = ArraySize(m_children); //--- Grow the children array by one slot ArrayResize(m_children, currentSize + 1); //--- Copy the new child into the new slot m_children[currentSize] = item; //--- Set the parent pointer on the new child m_children[currentSize].m_parent = GetPointer(this); //--- Return pointer to the new child slot return GetPointer(m_children[currentSize]); } //+---------------------------------------------------------------+ //| Serialize this value into a JSON string | //+---------------------------------------------------------------+ string SerializeToString() { //--- Build the output buffer string jsonString; //--- Run the recursive serializer SerializeToString(jsonString); //--- Return the assembled JSON string return jsonString; } //+---------------------------------------------------------------+ //| Serialize this value into an existing buffer | //+---------------------------------------------------------------+ void SerializeToString(string &jsonString, bool includeKey = false, bool includeComma = false) { //--- Skip undefined nodes entirely if(m_type == JsonUndefined) return; //--- Emit a separator comma when requested if(includeComma) jsonString += ","; //--- Emit the key prefix when this node lives in an object if(includeKey) jsonString += StringFormat("\"%s\":", m_key); //--- Cache child count for the array and object branches int numChildren = ArraySize(m_children); //--- Branch on this node's type switch(m_type) { case JsonNull: //--- Emit JSON null literal jsonString += "null"; break; case JsonBoolean: //--- Emit JSON true or false literal jsonString += (m_booleanValue ? "true" : "false"); break; case JsonInteger: //--- Emit integer literal jsonString += IntegerToString(m_integerValue); break; case JsonDouble: //--- Emit double literal jsonString += DoubleToString(m_doubleValue); break; case JsonString: { //--- Escape special characters before emitting string escaped = EscapeString(m_stringValue); //--- Emit quoted string when non-empty, else null jsonString += (StringLen(escaped) > 0) ? StringFormat("\"%s\"", escaped) : "null"; } break; case JsonArray: { //--- Emit array opening bracket jsonString += "["; //--- Recurse into each child without keys for(int index = 0; index < numChildren; index++) m_children[index].SerializeToString(jsonString, false, index > 0); //--- Emit array closing bracket jsonString += "]"; } break; case JsonObject: { //--- Emit object opening brace jsonString += "{"; //--- Recurse into each child including keys for(int index = 0; index < numChildren; index++) m_children[index].SerializeToString(jsonString, true, index > 0); //--- Emit object closing brace jsonString += "}"; } break; } } //+---------------------------------------------------------------+ //| Deserialize JSON content from a character array | //+---------------------------------------------------------------+ bool DeserializeFromArray(char &jsonCharacterArray[], int arrayLength, int ¤tIndex) { //--- Define the set of valid numeric characters string validNumericCharacters = "0123456789+-.eE"; //--- Track the start position of the current token int startPosition = currentIndex; //--- Walk through each character of the input for(; currentIndex < arrayLength; currentIndex++) { //--- Read the current character char currentCharacter = jsonCharacterArray[currentIndex]; //--- Stop when reaching a null terminator if(currentCharacter == 0) break; //--- Branch on the current character switch(currentCharacter) { case '\t': case '\r': case '\n': case ' ': //--- Skip whitespace and advance the token start startPosition = currentIndex + 1; break; case '[': { //--- Mark token start past the bracket startPosition = currentIndex + 1; //--- Reject if the type is already set if(m_type != JsonUndefined) return false; //--- Mark this node as an array m_type = JsonArray; //--- Advance past the opening bracket currentIndex++; //--- Build a child slot for parsing array entries JsonValue childValue(GetPointer(this), JsonUndefined); //--- Parse each child element until end of array while(childValue.DeserializeFromArray(jsonCharacterArray, arrayLength, currentIndex)) { //--- Append the parsed child when valid if(childValue.m_type != JsonUndefined) AddChildInternal(childValue); //--- Advance past numeric or array tokens if(childValue.m_type == JsonInteger || childValue.m_type == JsonDouble || childValue.m_type == JsonArray) currentIndex++; //--- Reset the child slot for the next iteration childValue.Reset(); //--- Re-link the child to this parent childValue.m_parent = GetPointer(this); //--- Stop on closing bracket if(jsonCharacterArray[currentIndex] == ']') break; //--- Advance past the comma separator currentIndex++; //--- Bail out if we ran past the end of input if(currentIndex >= arrayLength) return false; } //--- Verify the array closed properly return (jsonCharacterArray[currentIndex] == ']' || jsonCharacterArray[currentIndex] == 0); } case ']': //--- Validate that this closes a parent array return (m_parent && m_parent.m_type == JsonArray); case ':': { //--- Reject when no temporary key is staged if(m_temporaryKey == "") return false; //--- Build a placeholder child for the value JsonValue childValue(GetPointer(this), JsonUndefined); //--- Append it and capture the pointer JsonValue *addedChild = AddChildInternal(childValue); //--- Promote the temporary key onto the new child addedChild.m_key = m_temporaryKey; //--- Clear the staged key m_temporaryKey = ""; //--- Advance past the colon currentIndex++; //--- Recurse to parse the value portion if(!addedChild.DeserializeFromArray(jsonCharacterArray, arrayLength, currentIndex)) return false; } break; case ',': { //--- Mark token start past the comma startPosition = currentIndex + 1; //--- Reject when there is neither parent nor object context if(!m_parent && m_type != JsonObject) return false; //--- Reject when parent is not an array or object if(m_parent && m_parent.m_type != JsonArray && m_parent.m_type != JsonObject) return false; //--- Return up the stack when an array element completes if(m_parent && m_parent.m_type == JsonArray && m_type == JsonUndefined) return true; } break; case '{': { //--- Mark token start past the brace startPosition = currentIndex + 1; //--- Reject if the type is already set if(m_type != JsonUndefined) return false; //--- Mark this node as an object m_type = JsonObject; //--- Advance past the opening brace currentIndex++; //--- Recurse to parse object body if(!DeserializeFromArray(jsonCharacterArray, arrayLength, currentIndex)) return false; //--- Verify the object closed properly return (jsonCharacterArray[currentIndex] == '}' || jsonCharacterArray[currentIndex] == 0); } break; case '}': //--- Validate that this closes the current object return (m_type == JsonObject); case 't': case 'T': case 'f': case 'F': { //--- Reject if the type is already set if(m_type != JsonUndefined) return false; //--- Mark this node as boolean m_type = JsonBoolean; //--- Detect the literal 'true' if(currentIndex + 3 < arrayLength && StringCompare(GetSubstringFromArray(jsonCharacterArray, currentIndex, 4), "true", false) == 0) { //--- Store boolean as true m_booleanValue = true; //--- Advance past the literal currentIndex += 3; return true; } //--- Detect the literal 'false' if(currentIndex + 4 < arrayLength && StringCompare(GetSubstringFromArray(jsonCharacterArray, currentIndex, 5), "false", false) == 0) { //--- Store boolean as false m_booleanValue = false; //--- Advance past the literal currentIndex += 4; return true; } //--- Reject malformed boolean literal return false; } break; case 'n': case 'N': { //--- Reject if the type is already set if(m_type != JsonUndefined) return false; //--- Mark this node as null m_type = JsonNull; //--- Detect the literal 'null' if(currentIndex + 3 < arrayLength && StringCompare(GetSubstringFromArray(jsonCharacterArray, currentIndex, 4), "null", false) == 0) { //--- Advance past the literal currentIndex += 3; return true; } //--- Reject malformed null literal return false; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': case '+': case '.': { //--- Reject if the type is already set if(m_type != JsonUndefined) return false; //--- Track whether the number contains a decimal or exponent bool isDouble = false; //--- Capture the start of the numeric token int startOfNumber = currentIndex; //--- Scan forward through valid numeric characters while(jsonCharacterArray[currentIndex] != 0 && currentIndex < arrayLength) { //--- Advance to the next character currentIndex++; //--- Stop scanning when an invalid numeric character appears if(StringFind(validNumericCharacters, GetSubstringFromArray(jsonCharacterArray, currentIndex, 1)) < 0) break; //--- Detect a decimal point or exponent marker if(!isDouble) isDouble = (jsonCharacterArray[currentIndex] == '.' || jsonCharacterArray[currentIndex] == 'e' || jsonCharacterArray[currentIndex] == 'E'); } //--- Capture the numeric substring m_stringValue = GetSubstringFromArray(jsonCharacterArray, startOfNumber, currentIndex - startOfNumber); //--- Branch based on integer vs double if(isDouble) { //--- Mark the value as double m_type = JsonDouble; //--- Convert to double m_doubleValue = StringToDouble(m_stringValue); //--- Mirror as integer m_integerValue = (long)m_doubleValue; //--- Derive boolean from non-zero check m_booleanValue = m_integerValue != 0; } else { //--- Mark the value as integer m_type = JsonInteger; //--- Convert to integer m_integerValue = StringToInteger(m_stringValue); //--- Mirror as double m_doubleValue = (double)m_integerValue; //--- Derive boolean from non-zero check m_booleanValue = m_integerValue != 0; } //--- Step back so the outer loop sees the next token currentIndex--; return true; } break; case '\"': { //--- Branch when this string is a key inside an object if(m_type == JsonObject) { //--- Advance past the opening quote currentIndex++; //--- Capture the start of the key int startOfString = currentIndex; //--- Walk through the quoted key if(!ExtractStringFromArray(jsonCharacterArray, arrayLength, currentIndex)) return false; //--- Stage the parsed key for the next colon to consume m_temporaryKey = GetSubstringFromArray(jsonCharacterArray, startOfString, currentIndex - startOfString); } else { //--- Reject if the type is already set if(m_type != JsonUndefined) return false; //--- Mark this node as a string m_type = JsonString; //--- Advance past the opening quote currentIndex++; //--- Capture the start of the string content int startOfString = currentIndex; //--- Walk through the quoted string if(!ExtractStringFromArray(jsonCharacterArray, arrayLength, currentIndex)) return false; //--- Populate this value from the captured string SetFromString(JsonString, GetSubstringFromArray(jsonCharacterArray, startOfString, currentIndex - startOfString)); return true; } } break; } } //--- Return success when end of input is reached cleanly return true; } //+---------------------------------------------------------------+ //| Walk a quoted string and honor escape sequences | //+---------------------------------------------------------------+ bool ExtractStringFromArray(char &jsonCharacterArray[], int arrayLength, int ¤tIndex) { //--- Walk forward through the array until the closing quote for(; jsonCharacterArray[currentIndex] != 0 && currentIndex < arrayLength; currentIndex++) { //--- Read the current character char currentCharacter = jsonCharacterArray[currentIndex]; //--- Stop on an unescaped closing quote if(currentCharacter == '\"') break; //--- Handle escape sequences when a backslash appears if(currentCharacter == '\\' && currentIndex + 1 < arrayLength) { //--- Advance past the backslash currentIndex++; //--- Read the escape selector character currentCharacter = jsonCharacterArray[currentIndex]; //--- Branch on the escape selector switch(currentCharacter) { case '/': case '\\': case '\"': case 'b': case 'f': case 'r': case 'n': case 't': //--- Accept simple single-character escapes break; case 'u': { //--- Advance past the 'u' marker currentIndex++; //--- Walk through the four required hex digits for(int hexIndex = 0; hexIndex < 4 && currentIndex < arrayLength && jsonCharacterArray[currentIndex] != 0; hexIndex++, currentIndex++) { //--- Reject when a non-hex digit appears if(!((jsonCharacterArray[currentIndex] >= '0' && jsonCharacterArray[currentIndex] <= '9') || (jsonCharacterArray[currentIndex] >= 'A' && jsonCharacterArray[currentIndex] <= 'F') || (jsonCharacterArray[currentIndex] >= 'a' && jsonCharacterArray[currentIndex] <= 'f'))) return false; } //--- Step back so the outer loop advances correctly currentIndex--; break; } default: //--- Pass through unknown escapes silently break; } } } //--- Indicate successful traversal return true; } //+---------------------------------------------------------------+ //| Escape a string for JSON output | //+---------------------------------------------------------------+ string EscapeString(string value) { //--- Build input and output character buffers ushort inputCharacters[], escapedCharacters[]; //--- Convert the input string to a character array int inputLength = StringToShortArray(value, inputCharacters); //--- Reserve worst-case space (each character may double) if(ArrayResize(escapedCharacters, 2 * inputLength) != 2 * inputLength) return NULL; //--- Track the write position in the output buffer int escapedIndex = 0; //--- Walk through each input character for(int inputIndex = 0; inputIndex < inputLength; inputIndex++) { //--- Branch on the current character switch(inputCharacters[inputIndex]) { case '\\': //--- Emit escaped backslash escapedCharacters[escapedIndex] = '\\'; escapedIndex++; escapedCharacters[escapedIndex] = '\\'; escapedIndex++; break; case '"': //--- Emit escaped double quote escapedCharacters[escapedIndex] = '\\'; escapedIndex++; escapedCharacters[escapedIndex] = '"'; escapedIndex++; break; case '/': //--- Emit escaped forward slash escapedCharacters[escapedIndex] = '\\'; escapedIndex++; escapedCharacters[escapedIndex] = '/'; escapedIndex++; break; case 8: //--- Emit escaped backspace escapedCharacters[escapedIndex] = '\\'; escapedIndex++; escapedCharacters[escapedIndex] = 'b'; escapedIndex++; break; case 12: //--- Emit escaped form feed escapedCharacters[escapedIndex] = '\\'; escapedIndex++; escapedCharacters[escapedIndex] = 'f'; escapedIndex++; break; case '\n': //--- Emit escaped line feed escapedCharacters[escapedIndex] = '\\'; escapedIndex++; escapedCharacters[escapedIndex] = 'n'; escapedIndex++; break; case '\r': //--- Emit escaped carriage return escapedCharacters[escapedIndex] = '\\'; escapedIndex++; escapedCharacters[escapedIndex] = 'r'; escapedIndex++; break; case '\t': //--- Emit escaped tab escapedCharacters[escapedIndex] = '\\'; escapedIndex++; escapedCharacters[escapedIndex] = 't'; escapedIndex++; break; default: //--- Emit the character verbatim escapedCharacters[escapedIndex] = inputCharacters[inputIndex]; escapedIndex++; break; } } //--- Convert the output buffer back to a string return ShortArrayToString(escapedCharacters, 0, escapedIndex); } //+---------------------------------------------------------------+ //| Unescape a string parsed from JSON input | //+---------------------------------------------------------------+ string UnescapeString(string value) { //--- Build input and output character buffers ushort inputCharacters[], unescapedCharacters[]; //--- Convert the input string to a character array int inputLength = StringToShortArray(value, inputCharacters); //--- Reserve identical-length space (output never grows) if(ArrayResize(unescapedCharacters, inputLength) != inputLength) return NULL; //--- Track output and input positions int outputIndex = 0, inputIndex = 0; //--- Walk through each input character while(inputIndex < inputLength) { //--- Read the current character ushort currentCharacter = inputCharacters[inputIndex]; //--- Detect a backslash escape sequence if(currentCharacter == '\\' && inputIndex < inputLength - 1) { //--- Branch on the escape selector switch(inputCharacters[inputIndex + 1]) { case '\\': //--- Decode escaped backslash currentCharacter = '\\'; inputIndex++; break; case '"': //--- Decode escaped double quote currentCharacter = '"'; inputIndex++; break; case '/': //--- Decode escaped forward slash currentCharacter = '/'; inputIndex++; break; case 'b': //--- Decode escaped backspace currentCharacter = 8; inputIndex++; break; case 'f': //--- Decode escaped form feed currentCharacter = 12; inputIndex++; break; case 'n': //--- Decode escaped line feed currentCharacter = '\n'; inputIndex++; break; case 'r': //--- Decode escaped carriage return currentCharacter = '\r'; inputIndex++; break; case 't': //--- Decode escaped tab currentCharacter = '\t'; inputIndex++; break; } } //--- Write the decoded character to output unescapedCharacters[outputIndex] = currentCharacter; //--- Advance the output position outputIndex++; //--- Advance the input position inputIndex++; } //--- Convert the output buffer back to a string return ShortArrayToString(unescapedCharacters, 0, outputIndex); } }; //--- Initialize the static encoding code page to UTF-8 int JsonValue::encodingCodePage = CP_UTF8; #endif // AI_JSON_FILE_MQH //+------------------------------------------------------------------+