//+------------------------------------------------------------------+ //| JsonNode.mqh | //| Copyright 2026, Niquel Mendoza. | //| https://www.mql5.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza." #property link "https://www.mql5.com/" #property strict #ifndef JSONPARSERBYLEO_SRC_JSONNODE_MQH #define JSONPARSERBYLEO_SRC_JSONNODE_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "JsonParser.mqh" #include "JsonBuilder.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { // Layout cinta: // KEY 2 pos [tipo(8)|hash(32high)] [end(32low)|start(32high)] // STRING 2 pos [tipo(8)] [end(32low)|start(32high)] // INTEGER 2 pos [tipo(8)] [valor_long] // REAL 2 pos [tipo(8)] [valor_long (bits double)] // BOOLEAN 1 pos [tipo(8)|val(v)<<8] // NULL 1 pos [tipo(8)] // ARR 2 pos [tipo(8)|count(24)|end_abs(32high)] [start(32low)|end(32high)] // To string // OBJ 2 pos [tipo(8)|count(24)|end_abs(32high)] [start(32low)|end(32high)] // To string //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #define JSON_NODE_IS_NOT_VALID (m_ctx == NULL || m_idx == -1 || m_end == -1) struct CJsonIteratorArray; struct CJsonIteratorObj; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ struct CJsonNode { public: CJsonParser* m_ctx; int m_idx; int m_end; //--- CJsonNode() : m_ctx(NULL), m_idx(-1), m_end(-1) {} //--- CJsonNode(CJsonParser* _ctx, const int _idx, const int _end) : m_ctx(_ctx), m_idx(_idx), m_end(_end) {} //--- Acceso CJsonNode operator[](const string& key) const; CJsonNode At(const int index) const; CJsonNode AtHomegeneo(const int index) const; //--- Key de objeto por indice string AtObjKey(const int index) const; string AtObjKeyHomogeneo(const int index) const; //--- __forceinline bool HasKey(const string& key) const { return this[key].IsValid(); } //--- To string ToString(const string def = "") const; int ToStringUArray(uchar& data[]) const; long ToInt(const long def) const; bool ToIntSafe(long& val) const; double ToDouble(const double def) const; bool ToDoubleSafe(double& val) const; __forceinline bool ToBool(const bool def) const; bool ToBoolSafe(bool& val) const; //--- string ComplexToString() const; int ComplexToData(uchar& data[]) const; //--- Set (mutacion in-place, solo mismo tipo) bool SetInt(const long val); bool SetDbl(const double val); bool SetBool(const bool val); //--- Tipo __forceinline ENUM_JSON_VTYPE GetType() const { return ENUM_JSON_VTYPE(m_ctx.m_cinta[m_idx] & 0xFF); } //--- Is __forceinline bool IsValid() const; __forceinline bool IsNull() const; __forceinline bool IsArray() const; __forceinline bool IsObject() const; //--- Size (arr u obj) int Size() const; //--- Iteradores CJsonIteratorArray BeginArr() const; CJsonIteratorObj BeginObj() const; //--- Navegacion raw bool MoveToNextToken(const bool solo_en_el_bloque_actual = true); CJsonNode MoveNextPositions(const int posiciones, const bool solo_en_el_bloque_actual = true); bool MoveNextPositionsThis(const int posiciones, const bool solo_en_el_bloque_actual = true); }; //--- const CJsonNode EMPTY_JSON_NODE; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string CJsonNode::ComplexToString(void) const { const int t = int(m_ctx.m_cinta[m_idx] & 0xFF); if(t != JSON_VTYPE_OBJ && t != JSON_VTYPE_ARR) return ""; //--- const int start = int(m_ctx.m_cinta[m_idx + 1] & 0xFFFFFFFF); const int end = int(m_ctx.m_cinta[m_idx + 1] >> TSN_JSON_BIT_END_T); return CharArrayToString(m_ctx.m_json, start, (end - start) + 1); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CJsonNode::ComplexToData(uchar &data[]) const { const int t = int(m_ctx.m_cinta[m_idx] & 0xFF); if(t != JSON_VTYPE_OBJ && t != JSON_VTYPE_ARR) return 0; //--- const int start = int(m_ctx.m_cinta[m_idx + 1] & 0xFFFFFFFF); const int end = int(m_ctx.m_cinta[m_idx + 1] >> TSN_JSON_BIT_END_T); return ArrayCopy(data, m_ctx.m_json, start, 0, (end - start) + 1); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CJsonNode CJsonNode::operator[](const string& key) const { if(JSON_NODE_IS_NOT_VALID) return EMPTY_JSON_NODE; //--- if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_OBJ) return EMPTY_JSON_NODE; //--- Hash FNV-1a de la key buscada const int len = StringLen(key); uint hash = 2166136261; for(int k = 0; k < len; k++) { hash ^= (uint)key[k]; hash *= 16777619; } //--- Iterar pares KEY+VAL int cur = m_idx + 2; while(cur < m_end) { if(int(m_ctx.m_cinta[cur] & 0xFF) != JSON_VTYPE_KEY) { cur += m_ctx.GetStep(cur); continue; } if(uint(m_ctx.m_cinta[cur] >> TSN_JSON_BIT_STR_START_HASH) == hash) { const int val_pos = cur + 2; return CJsonNode(m_ctx, val_pos, val_pos + m_ctx.GetStep(val_pos)); } cur += 2; // salta KEY cur += m_ctx.GetStep(cur); // salta VAL } return EMPTY_JSON_NODE; } //+------------------------------------------------------------------+ //| At — acceso por indice en ARR | //+------------------------------------------------------------------+ CJsonNode CJsonNode::At(const int index) const { if(JSON_NODE_IS_NOT_VALID) return EMPTY_JSON_NODE; //--- if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_ARR) return EMPTY_JSON_NODE; //--- const int count = int((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 0xFFFFFF); if(index < 0 || index >= count) return EMPTY_JSON_NODE; //--- int cur = m_idx + 2; for(int k = 0; k < index; k++) cur += m_ctx.GetStep(cur); return CJsonNode(m_ctx, cur, cur + m_ctx.GetStep(cur)); } //+------------------------------------------------------------------+ //| AtHomegeneo — acceso por indice en ARR con step fijo | //+------------------------------------------------------------------+ CJsonNode CJsonNode::AtHomegeneo(const int index) const { if(JSON_NODE_IS_NOT_VALID) return EMPTY_JSON_NODE; //--- if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_ARR) return EMPTY_JSON_NODE; //--- const int count = int((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 0xFFFFFF); if(index < 0 || index >= count) return EMPTY_JSON_NODE; //--- const int first = m_idx + 2; const int step = m_ctx.GetStep(first); const int cur = first + index * step; return CJsonNode(m_ctx, cur, cur + step); } //+------------------------------------------------------------------+ //| AtObjKey — key string del par en posicion index dentro de OBJ | //+------------------------------------------------------------------+ string CJsonNode::AtObjKey(const int index) const { if(JSON_NODE_IS_NOT_VALID) return ""; //--- if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_OBJ) return ""; //--- const int count = int((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 0xFFFFFF); if(index < 0 || index >= count) return ""; //--- int cur = m_idx + 2; for(int k = 0; k < index; k++) { cur += 2; // salta KEY cur += m_ctx.GetStep(cur); // salta VAL } //--- // cur apunta al long[0] de KEY, avanzamos al long[1] (start/end) const int start = int(m_ctx.m_cinta[cur + 1] >> TSN_JSON_BIT_STR_START_END); const int end = int(m_ctx.m_cinta[cur + 1] & 0xFFFFFFFF); return CharArrayToString(m_ctx.m_json, start, (end - start) + 1); } //+------------------------------------------------------------------+ //| AtObjKeyHomogeneo — igual pero asume step fijo del VAL | //+------------------------------------------------------------------+ string CJsonNode::AtObjKeyHomogeneo(const int index) const { if(JSON_NODE_IS_NOT_VALID) return ""; //--- if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_OBJ) return ""; //--- const int count = int((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 0xFFFFFF); if(index < 0 || index >= count) return ""; //--- step fijo = 2(KEY) + step(primer VAL) // [OBJ][OPBJ][KEY][KEY][VAL] // 10 11 12 13 14 const int first_val = m_idx + 4; const int val_step = m_ctx.GetStep(first_val); const int pair_step = 2 + val_step; const int key_pos = (m_idx + 2) + index * pair_step; //--- const int start = int(m_ctx.m_cinta[key_pos + 1] >> TSN_JSON_BIT_STR_START_END); const int end = int(m_ctx.m_cinta[key_pos + 1] & 0xFFFFFFFF); return CharArrayToString(m_ctx.m_json, start, (end - start) + 1); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CJsonNode::ToStringUArray(uchar &data[]) const { const int start = int(m_ctx.m_cinta[m_idx + 1] >> TSN_JSON_BIT_STR_START_END); const int end = int(m_ctx.m_cinta[m_idx + 1] & 0xFFFFFFFF); return m_ctx.Unescape(start, end, data); } //+------------------------------------------------------------------+ //| ToString | //+------------------------------------------------------------------+ string CJsonNode::ToString(const string def = "") const { if(JSON_NODE_IS_NOT_VALID) return def; //--- switch(int(m_ctx.m_cinta[m_idx] & 0xFF)) { case JSON_VTYPE_INTEGER: return string(m_ctx.m_cinta[m_idx + 1]); case JSON_VTYPE_REAL: { static BitInterpreter un; un.long_value = m_ctx.m_cinta[m_idx + 1]; return string(un.double_value); } case JSON_VTYPE_BOOLEAN: return ((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 1) != 0 ? "true" : "false"; case JSON_VTYPE_STRING: { const int start = int(m_ctx.m_cinta[m_idx + 1] >> TSN_JSON_BIT_STR_START_END); const int end = int(m_ctx.m_cinta[m_idx + 1] & 0xFFFFFFFF); return m_ctx.Unescape(start, end); } case JSON_VTYPE_NULL: return def; } return def; } //+------------------------------------------------------------------+ //| ToInt | //+------------------------------------------------------------------+ long CJsonNode::ToInt(const long def) const { if(JSON_NODE_IS_NOT_VALID) return def; //--- switch(int(m_ctx.m_cinta[m_idx] & 0xFF)) { case JSON_VTYPE_INTEGER: return m_ctx.m_cinta[m_idx + 1]; case JSON_VTYPE_REAL: { static BitInterpreter un; un.long_value = m_ctx.m_cinta[m_idx + 1]; return long(un.double_value); } case JSON_VTYPE_BOOLEAN: return long((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 1); } return def; } //+------------------------------------------------------------------+ bool CJsonNode::ToIntSafe(long& val) const { if(JSON_NODE_IS_NOT_VALID) return false; //--- switch(int(m_ctx.m_cinta[m_idx] & 0xFF)) { case JSON_VTYPE_INTEGER: val = m_ctx.m_cinta[m_idx + 1]; return true; case JSON_VTYPE_REAL: { static BitInterpreter un; un.long_value = m_ctx.m_cinta[m_idx + 1]; val = long(un.double_value); return true; } case JSON_VTYPE_BOOLEAN: val = long((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 1); return true; } return false; } //+------------------------------------------------------------------+ //| ToDouble | //+------------------------------------------------------------------+ double CJsonNode::ToDouble(const double def) const { if(JSON_NODE_IS_NOT_VALID) return def; //--- switch(int(m_ctx.m_cinta[m_idx] & 0xFF)) { case JSON_VTYPE_INTEGER: return double(m_ctx.m_cinta[m_idx + 1]); case JSON_VTYPE_REAL: { static BitInterpreter un; un.long_value = m_ctx.m_cinta[m_idx + 1]; return un.double_value; } case JSON_VTYPE_BOOLEAN: return double((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 1); } return def; } //+------------------------------------------------------------------+ bool CJsonNode::ToDoubleSafe(double& val) const { if(JSON_NODE_IS_NOT_VALID) return false; //--- switch(int(m_ctx.m_cinta[m_idx] & 0xFF)) { case JSON_VTYPE_INTEGER: val = double(m_ctx.m_cinta[m_idx + 1]); return true; case JSON_VTYPE_REAL: { static BitInterpreter un; un.long_value = m_ctx.m_cinta[m_idx + 1]; val = un.double_value; return true; } case JSON_VTYPE_BOOLEAN: val = double((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 1); return true; } return false; } //+------------------------------------------------------------------+ //| ToBool | //+------------------------------------------------------------------+ __forceinline bool CJsonNode::ToBool(const bool def) const { if(JSON_NODE_IS_NOT_VALID) return def; //--- switch(int(m_ctx.m_cinta[m_idx] & 0xFF)) { case JSON_VTYPE_INTEGER: return m_ctx.m_cinta[m_idx + 1] != 0; case JSON_VTYPE_REAL: { static BitInterpreter un; un.long_value = m_ctx.m_cinta[m_idx + 1]; return un.double_value != 0.0; } case JSON_VTYPE_BOOLEAN: return ((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 1) != 0; } return def; } //+------------------------------------------------------------------+ bool CJsonNode::ToBoolSafe(bool& val) const { if(JSON_NODE_IS_NOT_VALID) return false; //--- switch(int(m_ctx.m_cinta[m_idx] & 0xFF)) { case JSON_VTYPE_INTEGER: val = m_ctx.m_cinta[m_idx + 1] != 0; return true; case JSON_VTYPE_REAL: { static BitInterpreter un; un.long_value = m_ctx.m_cinta[m_idx + 1]; val = un.double_value != 0.0; return true; } case JSON_VTYPE_BOOLEAN: val = ((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 1) != 0; return true; } return false; } //+------------------------------------------------------------------+ //| SetInt | //+------------------------------------------------------------------+ bool CJsonNode::SetInt(const long val) { if(JSON_NODE_IS_NOT_VALID) return false; if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_INTEGER) return false; m_ctx.m_cinta[m_idx + 1] = val; return true; } //+------------------------------------------------------------------+ //| SetDbl | //+------------------------------------------------------------------+ bool CJsonNode::SetDbl(const double val) { if(JSON_NODE_IS_NOT_VALID) return false; if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_REAL) return false; static BitInterpreter un; un.double_value = val; m_ctx.m_cinta[m_idx + 1] = un.long_value; return true; } //+------------------------------------------------------------------+ //| SetBool | //+------------------------------------------------------------------+ bool CJsonNode::SetBool(const bool val) { if(JSON_NODE_IS_NOT_VALID) return false; //--- if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_BOOLEAN) return false; //--- m_ctx.m_cinta[m_idx] = JSON_VTYPE_BOOLEAN | long(val) << TSN_JSON_BIT_START_ENDTYPE; return true; } //+------------------------------------------------------------------+ //| Size | //+------------------------------------------------------------------+ int CJsonNode::Size() const { if(JSON_NODE_IS_NOT_VALID) return 0; //--- const int tipo = int(m_ctx.m_cinta[m_idx] & 0xFF); if(tipo != JSON_VTYPE_ARR && tipo != JSON_VTYPE_OBJ) return 0; //--- return int((m_ctx.m_cinta[m_idx] >> TSN_JSON_BIT_START_ENDTYPE) & 0xFFFFFF); } //+------------------------------------------------------------------+ //| Is | //+------------------------------------------------------------------+ __forceinline bool CJsonNode::IsValid() const { return !JSON_NODE_IS_NOT_VALID; } //+------------------------------------------------------------------+ __forceinline bool CJsonNode::IsNull() const { if(JSON_NODE_IS_NOT_VALID) return true; return int(m_ctx.m_cinta[m_idx] & 0xFF) == JSON_VTYPE_NULL; } //+------------------------------------------------------------------+ __forceinline bool CJsonNode::IsArray() const { if(JSON_NODE_IS_NOT_VALID) return false; return int(m_ctx.m_cinta[m_idx] & 0xFF) == JSON_VTYPE_ARR; } //+------------------------------------------------------------------+ __forceinline bool CJsonNode::IsObject() const { if(JSON_NODE_IS_NOT_VALID) return false; return int(m_ctx.m_cinta[m_idx] & 0xFF) == JSON_VTYPE_OBJ; } //+------------------------------------------------------------------+ //| Navegacion raw | //+------------------------------------------------------------------+ bool CJsonNode::MoveToNextToken(const bool solo_en_el_bloque_actual = true) { if(JSON_NODE_IS_NOT_VALID) return false; //--- const int next_pos = m_idx + m_ctx.GetStep(m_idx); const int final_pos = next_pos + m_ctx.GetStep(next_pos); //--- if(next_pos >= (solo_en_el_bloque_actual ? m_end : m_ctx.m_cinta_pos)) return false; //--- m_idx = next_pos; m_end = final_pos; return true; } //+------------------------------------------------------------------+ CJsonNode CJsonNode::MoveNextPositions(const int posiciones, const bool solo_en_el_bloque_actual = true) { if(JSON_NODE_IS_NOT_VALID) return EMPTY_JSON_NODE; //--- const int next_pos = m_idx + posiciones; const int final_pos = solo_en_el_bloque_actual ? m_end : next_pos + m_ctx.GetStep(next_pos); //--- if(next_pos >= (solo_en_el_bloque_actual ? m_end : m_ctx.m_cinta_pos)) return EMPTY_JSON_NODE; //--- return CJsonNode(m_ctx, next_pos, final_pos); } //+------------------------------------------------------------------+ bool CJsonNode::MoveNextPositionsThis(const int posiciones, const bool solo_en_el_bloque_actual = true) { if(JSON_NODE_IS_NOT_VALID) return false; //--- const int next_pos = m_idx + posiciones; const int final_pos = solo_en_el_bloque_actual ? m_end : next_pos + m_ctx.GetStep(next_pos); //--- if(next_pos >= (solo_en_el_bloque_actual ? m_end : m_ctx.m_cinta_pos)) return false; //--- m_idx = next_pos; m_end = final_pos; return true; } //+------------------------------------------------------------------+ //| Iterador de arrays | //+------------------------------------------------------------------+ struct CJsonIteratorArray { CJsonParser* m_ctx; int m_cur; const int m_end; //--- CJsonIteratorArray(CJsonParser* ctx, const int cur, const int end) : m_ctx(ctx), m_cur(cur), m_end(end) {} //--- CJsonIteratorArray() : m_ctx(NULL), m_cur(0), m_end(0) {} //--- __forceinline bool IsValid() { return m_cur < m_end; } //--- void Next() { if(m_cur >= m_end) return; m_cur += m_ctx.GetStep(m_cur); } //--- CJsonNode Val() { if(m_cur >= m_end) return EMPTY_JSON_NODE; const int fin = m_cur + m_ctx.GetStep(m_cur); return CJsonNode(m_ctx, m_cur, fin); } }; const CJsonIteratorArray EMPTY_JSON_ITERATOR_ARR; //+------------------------------------------------------------------+ //| Iterador de objetos | //+------------------------------------------------------------------+ struct CJsonIteratorObj { CJsonParser* m_ctx; int m_cur; const int m_end; //--- CJsonIteratorObj(CJsonParser* ctx, const int cur, const int end) : m_ctx(ctx), m_cur(cur), m_end(end) {} //--- CJsonIteratorObj() : m_ctx(NULL), m_cur(0), m_end(0) {} //--- __forceinline bool IsValid() { return m_cur < m_end; } //--- void Next() { if(m_cur >= m_end) return; m_cur += 2; // salta KEY (2 pos) m_cur += m_ctx.GetStep(m_cur); // salta VAL } //--- string Key() { if(m_cur >= m_end) return ""; const int s = int(m_ctx.m_cinta[m_cur + 1] >> TSN_JSON_BIT_STR_START_END); const int e = int(m_ctx.m_cinta[m_cur + 1] & 0xFFFFFFFF); return CharArrayToString(m_ctx.m_json, s, (e - s) + 1); } //--- CJsonNode Val() { if(m_cur >= m_end) return EMPTY_JSON_NODE; const int pos = m_cur + 2; const int fin = pos + m_ctx.GetStep(pos); return CJsonNode(m_ctx, pos, fin); } }; const CJsonIteratorObj EMPTY_JSON_ITERATOR_OBJ; //+------------------------------------------------------------------+ //| BeginArr / BeginObj | //+------------------------------------------------------------------+ CJsonIteratorArray CJsonNode::BeginArr() const { if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_ARR) return EMPTY_JSON_ITERATOR_ARR; return CJsonIteratorArray(m_ctx, m_idx + 2, m_end); } //+------------------------------------------------------------------+ CJsonIteratorObj CJsonNode::BeginObj() const { if(int(m_ctx.m_cinta[m_idx] & 0xFF) != JSON_VTYPE_OBJ) return EMPTY_JSON_ITERATOR_OBJ; return CJsonIteratorObj(m_ctx, m_idx + 2, m_end); } //--- } //+------------------------------------------------------------------+ #endif // JSONPARSERBYLEO_SRC_JSONNODE_MQH