//+------------------------------------------------------------------+
//|                                                 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 &currentIndex)
     {
      //--- 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 &currentIndex)
     {
      //--- 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
//+------------------------------------------------------------------+