//+------------------------------------------------------------------+ //| JsonDomSerializer.mqh | //| Copyright 2026, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd." #property link "https://www.mql5.com" #property strict #ifndef JSONPARSERBYLEO_SRC_JSONDOMSERIALIZER_MQH #define JSONPARSERBYLEO_SRC_JSONDOMSERIALIZER_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include #include "JsonDefines.mqh" #include "BuilderDefs.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CJsonDomSerializer { public: //--- uchar m_buf[]; int m_buf_s; int m_buf_r; uchar m_dbl_digits; private: //-- stacks CHashMapFastIterator(string, CDomNodeBase*) m_stack_it_obj[512]; int m_stack_count[512]; int m_stack_ix[512]; int m_stack_k[512]; int m_stack_it_obj_curr; //--- void EscapeString(const int l, const string& s); public: CJsonDomSerializer(void); ~CJsonDomSerializer(void) {} //--- bool Serialize(CDomNodeBase* v); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CJsonDomSerializer::CJsonDomSerializer() : m_buf_s(0), m_buf_r(4096), m_dbl_digits(8), m_stack_it_obj_curr(0) { ArrayResize(m_buf, m_buf_r, m_buf_r); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CJsonDomSerializer::EscapeString(const int l, const string &s) { for(int i = 0; i < l; i++) { const ushort c = s[i]; //--- Unicode > 0xFF: \uXXXX con los 4 nibbles completos if(c > 0xFF) { m_buf[m_buf_s++] = '\\'; m_buf[m_buf_s++] = 'u'; uchar t; t = uchar((c >> 12) & 0xF); m_buf[m_buf_s++] = (uchar)(t < 10 ? '0' + t : 'a' + t - 10); t = uchar((c >> 8) & 0xF); m_buf[m_buf_s++] = (uchar)(t < 10 ? '0' + t : 'a' + t - 10); t = uchar((c >> 4) & 0xF); m_buf[m_buf_s++] = (uchar)(t < 10 ? '0' + t : 'a' + t - 10); t = uchar(c & 0xF); m_buf[m_buf_s++] = (uchar)(t < 10 ? '0' + t : 'a' + t - 10); continue; } //--- 0x00–0xFF: lookup en tabla const uchar esc = g_tsn_table_json_escape_tables[c]; if(esc == TSN_TABLE_ESCAPE_PASSTHROUGH) { m_buf[m_buf_s++] = (uchar)c; } else if(esc == TSN_TABLE_ESCAPE_UNICODE) { m_buf[m_buf_s++] = '\\'; m_buf[m_buf_s++] = 'u'; m_buf[m_buf_s++] = '0'; m_buf[m_buf_s++] = '0'; uchar t; t = uchar((c >> 4) & 0xF); m_buf[m_buf_s++] = (uchar)(t < 10 ? '0' + t : 'a' + t - 10); t = uchar(c & 0xF); m_buf[m_buf_s++] = (uchar)(t < 10 ? '0' + t : 'a' + t - 10); } else { //--- letra dedicada: \n \t \\ \" etc. m_buf[m_buf_s++] = '\\'; m_buf[m_buf_s++] = esc; } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CJsonDomSerializer::Serialize(CDomNodeBase* v) { //--- #define DOMMANAGERCHEKCAP_JSON(N) \ if(m_buf_s + (N) >= m_buf_r) \ { \ m_buf_r = (m_buf_s + (N)) << 2; \ ArrayResize(m_buf, m_buf_r, m_buf_r); \ } //--- int next = v.m_type; m_stack_it_obj_curr = -1; m_buf_s = 0; //--- while(true) { switch(next) { case 0: break; // Sale noma.. case TSN_SBL_BASE_TYPE_OBJ: { //--- m_stack_it_obj_curr++; m_stack_it_obj[m_stack_it_obj_curr] = v.HashMap().BeginIteration(); m_stack_ix[m_stack_it_obj_curr] = -1; // no usar m_stack_k[m_stack_it_obj_curr] = 0; //--- DOMMANAGERCHEKCAP_JSON(1) m_buf[m_buf_s++] = '{'; break; } case TSN_SBL_BASE_TYPE_ARR: { //--- m_stack_it_obj_curr++; m_stack_ix[m_stack_it_obj_curr] = v.m_v.ix; m_stack_count[m_stack_it_obj_curr] = v.Size(); m_stack_k[m_stack_it_obj_curr] = 0; //--- DOMMANAGERCHEKCAP_JSON(1) m_buf[m_buf_s++] = '['; break; } case TSN_SBL_BASE_TYPE_INT: { //--- const long value = v.m_v.iv; DOMMANAGERCHEKCAP_JSON(13) //--- if(value == 0) { m_buf[m_buf_s++] = '0'; break; } //-- int pos = 0; ulong uvalue; if(value < 0) { m_buf[m_buf_s++] = '-'; uvalue = ulong(-(value + 1)) + 1; } else { uvalue = ulong(value); } //--- static uchar digits[24]; int n = 0; //--- while(uvalue >= 100) { const ulong idx = (uvalue % 100ULL) << 1ULL; digits[n++] = g_tsn_digit_pairs[idx + 1]; digits[n++] = g_tsn_digit_pairs[idx]; uvalue /= 100ULL; } //--- if(uvalue >= 10) { const ulong idx = (uvalue << 1); digits[n++] = g_tsn_digit_pairs[idx + 1]; digits[n++] = g_tsn_digit_pairs[idx]; } else { digits[n++] = uchar('0' + uvalue); } //--- for(int j = n - 1; j >= 0; j--) m_buf[m_buf_s++] = digits[j]; //--- break; } case TSN_SBL_BASE_TYPE_FLT: { //--- double value = v.m_v.dv; if(!MathIsValidNumber(value)) { m_buf[m_buf_s++] = 'n'; m_buf[m_buf_s++] = 'u'; m_buf[m_buf_s++] = 'l'; m_buf[m_buf_s++] = 'l'; break; // Salimos.. } //--- if(value < 0) { m_buf[m_buf_s++] = '-'; value = -value; } //--- const double factor = g_Pow10[m_dbl_digits]; const ulong escalado = (ulong)MathRound(value * factor); ulong ent = escalado / (ulong)factor; ulong dec = escalado % (ulong)factor; // --- parte entera, mismo algoritmo de 2-en-2 que ya usas --- if(ent == 0) { m_buf[m_buf_s++] = '0'; } else { static uchar dig_buff[24]; int n = 0; //--- while(ent >= 100) { const ulong idx = (ent % 100ULL) << 1ULL; dig_buff[n++] = g_tsn_digit_pairs[idx + 1]; dig_buff[n++] = g_tsn_digit_pairs[idx]; ent /= 100ULL; } if(ent >= 10) { const ulong idx = ent << 1ULL; dig_buff[n++] = g_tsn_digit_pairs[idx + 1]; dig_buff[n++] = g_tsn_digit_pairs[idx]; } else { dig_buff[n++] = (uchar)('0' + ent); } for(int j = n - 1; j >= 0; j--) m_buf[m_buf_s++] = dig_buff[j]; } //--- punto decimal --- m_buf[m_buf_s++] = '.'; //--- parte decimal, con ceros a la izquierda (padding fijo a 'digits') --- static uchar dec_buff[24]; int dn = 0; //--- if(dec == 0) { dec_buff[dn++] = '0'; } else { while(dec >= 100) { const ulong idx = (dec % 100ULL) << 1ULL; dec_buff[dn++] = g_tsn_digit_pairs[idx + 1]; dec_buff[dn++] = g_tsn_digit_pairs[idx]; dec /= 100ULL; } if(dec >= 10) { const ulong idx = dec << 1ULL; dec_buff[dn++] = g_tsn_digit_pairs[idx + 1]; dec_buff[dn++] = g_tsn_digit_pairs[idx]; } else { dec_buff[dn++] = (uchar)('0' + dec); } } //--- // padding de ceros a la izquierda para completar 'm_dbl_digits' posiciones const int padding = m_dbl_digits - dn; for(int p = 0; p < padding; p++) m_buf[m_buf_s++] = '0'; for(int j = dn - 1; j >= 0; j--) m_buf[m_buf_s++] = dec_buff[j]; //--- break; } case TSN_SBL_BASE_TYPE_BOL: { DOMMANAGERCHEKCAP_JSON(7) if(v.m_v.bv) { m_buf[m_buf_s++] = 't'; m_buf[m_buf_s++] = 'r'; m_buf[m_buf_s++] = 'u'; m_buf[m_buf_s++] = 'e'; } else { m_buf[m_buf_s++] = 'f'; m_buf[m_buf_s++] = 'a'; m_buf[m_buf_s++] = 'l'; m_buf[m_buf_s++] = 's'; m_buf[m_buf_s++] = 'e'; } break; } case TSN_SBL_BASE_TYPE_STR: { //--- const int i = v.m_v.ix; const int l = StringLen(v.m_ctx.m_string_stack[i]); const int r = ((l << 2) + (l << 1)) + 3; DOMMANAGERCHEKCAP_JSON(r) //--- m_buf[m_buf_s++] = '"'; EscapeString(l, v.m_ctx.m_string_stack[i]); m_buf[m_buf_s++] = '"'; //--- break; } case TSN_SBL_BASE_DOM_NULL: { DOMMANAGERCHEKCAP_JSON(6) m_buf[m_buf_s++] = 'n'; m_buf[m_buf_s++] = 'u'; m_buf[m_buf_s++] = 'l'; m_buf[m_buf_s++] = 'l'; break; } default: return false; } //---- if(m_stack_ix[m_stack_it_obj_curr] == -1) { if(m_stack_it_obj[m_stack_it_obj_curr].IsValid()) { if(m_stack_k[m_stack_it_obj_curr] > 0) m_buf[m_buf_s++] = ','; //--- Key const int l = m_stack_it_obj[m_stack_it_obj_curr].KeyLen(); const int r = ((l << 2) + (l << 1)) + 3; DOMMANAGERCHEKCAP_JSON(r) //--- Escape m_buf[m_buf_s++] = '"'; EscapeString(l, m_stack_it_obj[m_stack_it_obj_curr].m_ctx.m_table[m_stack_it_obj[m_stack_it_obj_curr].m_current_slot].key); m_buf[m_buf_s++] = '"'; m_buf[m_buf_s++] = ':'; //--- Value v = m_stack_it_obj[m_stack_it_obj_curr].Val(); // assing.. m_stack_it_obj[m_stack_it_obj_curr].Next(); next = v.m_type; //--- m_stack_k[m_stack_it_obj_curr]++; continue; } // Ya no.. DOMMANAGERCHEKCAP_JSON(2) m_buf[m_buf_s++] = '}'; } else { if(m_stack_k[m_stack_it_obj_curr] < m_stack_count[m_stack_it_obj_curr]) { //--- if(m_stack_k[m_stack_it_obj_curr] > 0) m_buf[m_buf_s++] = ','; //--- Avanzamos.. v = v.m_ctx.m_node[m_stack_ix[m_stack_it_obj_curr]].values[m_stack_k[m_stack_it_obj_curr]++]; // assing.. next = v.m_type; continue; } // Ya no ... DOMMANAGERCHEKCAP_JSON(2) m_buf[m_buf_s++] = ']'; } //--- m_stack_it_obj_curr--; // Baja if(m_stack_it_obj_curr < 0) return true; //--- next = 0; // break y vuleve a evaluar denbuvo... } //--- return false; } } //+------------------------------------------------------------------+ #endif // JSONPARSERBYLEO_SRC_JSONDOMSERIALIZER_MQH