2026-08-27 13:56:43 -05:00 | | | //+------------------------------------------------------------------+
|
2026-07-19 15:56:41 -05:00 | | | //| JsonDomSerializer.mqh |
|
| | | //| Copyright 2026, MetaQuotes Ltd. |
|
| | | //| https://www.mql5.com |
|
| | | //+------------------------------------------------------------------+
|
| | | #property copyright "Copyright 2026, MetaQuotes Ltd."
|
| | | #property link "https://www.mql5.com"
|
| | | #property strict
|
| | |
|
2026-07-19 18:32:50 -05:00 | | | #ifndef JSONPARSERBYLEO_SRC_JSONDOMSERIALIZER_MQH
|
| | | #define JSONPARSERBYLEO_SRC_JSONDOMSERIALIZER_MQH
|
| | |
|
2026-07-19 15:56:41 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-07-19 18:32:50 -05:00 | | | #include <TSN\\BSFL\\Node.mqh>
|
| | | #include "JsonDefines.mqh"
|
| | | #include "BuilderDefs.mqh"
|
2026-07-19 15:56:41 -05:00 | | |
|
2026-07-19 18:32:50 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-07-19 15:56:41 -05:00 | | | namespace TSN
|
| | | {
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-07-20 09:30:41 -05:00 | | | class CJsonDomSerializer
|
2026-07-19 15:56:41 -05:00 | | | {
|
2026-07-19 18:32:50 -05:00 | | | public:
|
| | | //---
|
| | | uchar m_buf[];
|
| | | int m_buf_s;
|
| | | int m_buf_r;
|
| | | uchar m_dbl_digits;
|
| | |
|
2026-07-19 15:56:41 -05:00 | | | private:
|
2026-07-19 18:32:50 -05:00 | | | //-- stacks
|
2026-08-27 13:56:43 -05:00 | | | CHashMapFastIterator(string, CDomNodeBase*) m_stack_it_obj[JSONPARSERBYLEO_MAX_STACK];
|
| | | int m_stack_count[JSONPARSERBYLEO_MAX_STACK];
|
| | | int m_stack_ix[JSONPARSERBYLEO_MAX_STACK];
|
| | | int m_stack_k[JSONPARSERBYLEO_MAX_STACK];
|
2026-07-19 18:32:50 -05:00 | | | int m_stack_it_obj_curr;
|
| | |
|
| | | //---
|
| | | void EscapeString(const int l, const string& s);
|
2026-07-19 15:56:41 -05:00 | | |
|
| | | public:
|
2026-07-20 09:30:41 -05:00 | | | CJsonDomSerializer(void);
|
| | | ~CJsonDomSerializer(void) {}
|
2026-07-19 15:56:41 -05:00 | | |
|
| | | //---
|
| | | bool Serialize(CDomNodeBase* v);
|
| | | };
|
| | |
|
2026-07-19 18:32:50 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-07-20 09:30:41 -05:00 | | | CJsonDomSerializer::CJsonDomSerializer()
|
2026-07-19 18:32:50 -05:00 | | | : m_buf_s(0), m_buf_r(4096), m_dbl_digits(8), m_stack_it_obj_curr(0)
|
2026-07-19 15:56:41 -05:00 | | | {
|
2026-07-19 18:32:50 -05:00 | | | ArrayResize(m_buf, m_buf_r, m_buf_r);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-07-20 09:30:41 -05:00 | | | void CJsonDomSerializer::EscapeString(const int l, const string &s)
|
2026-07-19 18:32:50 -05:00 | | | {
|
| | | for(int i = 0; i < l; i++)
|
2026-07-19 15:56:41 -05:00 | | | {
|
2026-07-19 18:32:50 -05:00 | | | const ushort c = s[i];
|
2026-07-19 15:56:41 -05:00 | | |
|
2026-07-19 18:32:50 -05:00 | | | //--- Unicode > 0xFF: \uXXXX con los 4 nibbles completos
|
| | | if(c > 0xFF)
|
2026-07-19 15:56:41 -05:00 | | | {
|
2026-07-19 18:32:50 -05:00 | | | 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;
|
2026-07-19 15:56:41 -05:00 | | | }
|
| | |
|
2026-07-19 18:32:50 -05:00 | | | //--- 0x00–0xFF: lookup en tabla
|
2026-07-19 18:50:54 -05:00 | | | const uchar esc = g_tsn_table_json_escape_tables[c];
|
2026-07-19 18:32:50 -05:00 | | | if(esc == TSN_TABLE_ESCAPE_PASSTHROUGH)
|
2026-07-19 15:56:41 -05:00 | | | {
|
2026-07-19 18:32:50 -05:00 | | | m_buf[m_buf_s++] = (uchar)c;
|
2026-07-19 15:56:41 -05:00 | | | }
|
2026-07-19 18:32:50 -05:00 | | | 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;
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-07-20 09:30:41 -05:00 | | | bool CJsonDomSerializer::Serialize(CDomNodeBase* v)
|
2026-07-19 18:32:50 -05:00 | | | {
|
| | | //---
|
| | | #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;
|
2026-07-20 09:30:41 -05:00 | | | m_stack_it_obj_curr = -1;
|
2026-07-19 19:15:32 -05:00 | | | m_buf_s = 0;
|
2026-07-19 18:32:50 -05:00 | | |
|
| | | //---
|
| | | while(true)
|
| | | {
|
| | | switch(next)
|
2026-07-19 15:56:41 -05:00 | | | {
|
2026-07-19 18:32:50 -05:00 | | | case 0:
|
| | | break; // Sale noma..
|
| | | case TSN_SBL_BASE_TYPE_OBJ:
|
| | | {
|
| | | //---
|
2026-07-20 09:30:41 -05:00 | | | 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
|
2026-07-19 18:32:50 -05:00 | | | m_stack_k[m_stack_it_obj_curr] = 0;
|
| | |
|
| | | //---
|
| | | DOMMANAGERCHEKCAP_JSON(1)
|
| | | m_buf[m_buf_s++] = '{';
|
| | | break;
|
| | | }
|
| | | case TSN_SBL_BASE_TYPE_ARR:
|
| | | {
|
| | | //---
|
2026-07-20 09:30:41 -05:00 | | | 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();
|
2026-07-19 18:32:50 -05:00 | | | 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;
|
| | | }
|
2026-07-19 15:56:41 -05:00 | | |
|
2026-07-19 18:32:50 -05:00 | | | //---
|
| | | 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;
|
2026-07-20 09:30:41 -05:00 | | | const int l = StringLen(v.m_ctx.m_string_stack[i]);
|
2026-07-19 18:32:50 -05:00 | | | const int r = ((l << 2) + (l << 1)) + 3;
|
| | | DOMMANAGERCHEKCAP_JSON(r)
|
| | |
|
| | | //---
|
| | | m_buf[m_buf_s++] = '"';
|
2026-07-20 09:30:41 -05:00 | | | EscapeString(l, v.m_ctx.m_string_stack[i]);
|
2026-07-19 18:32:50 -05:00 | | | 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;
|
2026-07-19 15:56:41 -05:00 | | | }
|
2026-07-19 18:32:50 -05:00 | | |
|
| | | //----
|
2026-07-20 09:30:41 -05:00 | | | if(m_stack_ix[m_stack_it_obj_curr] == -1)
|
2026-07-19 15:56:41 -05:00 | | | {
|
2026-07-19 18:32:50 -05:00 | | | 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
|
2026-07-23 16:13:49 -05:00 | | | static string out;
|
| | | m_stack_it_obj[m_stack_it_obj_curr].Key(out);
|
| | |
|
| | | //---
|
| | | const int l = StringLen(out);
|
2026-07-19 18:32:50 -05:00 | | | const int r = ((l << 2) + (l << 1)) + 3;
|
| | | DOMMANAGERCHEKCAP_JSON(r)
|
| | |
|
| | | //--- Escape
|
| | | m_buf[m_buf_s++] = '"';
|
2026-07-23 16:13:49 -05:00 | | | EscapeString(l, out);
|
2026-07-19 18:32:50 -05:00 | | | 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;
|
2026-07-19 15:56:41 -05:00 | | |
|
2026-07-19 18:32:50 -05:00 | | | //---
|
| | | m_stack_k[m_stack_it_obj_curr]++;
|
| | | continue;
|
| | | }
|
| | | // Ya no..
|
| | | DOMMANAGERCHEKCAP_JSON(2)
|
| | | m_buf[m_buf_s++] = '}';
|
2026-07-19 15:56:41 -05:00 | | | }
|
2026-07-19 18:32:50 -05:00 | | | else
|
2026-07-19 15:56:41 -05:00 | | | {
|
2026-07-19 18:32:50 -05:00 | | | 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++] = ',';
|
2026-07-19 15:56:41 -05:00 | | |
|
2026-07-19 18:32:50 -05:00 | | | //--- Avanzamos..
|
2026-07-20 09:30:41 -05:00 | | | v = v.m_ctx.m_node[m_stack_ix[m_stack_it_obj_curr]].values[m_stack_k[m_stack_it_obj_curr]++]; // assing..
|
2026-07-19 18:32:50 -05:00 | | | next = v.m_type;
|
| | | continue;
|
| | | }
|
| | | // Ya no ...
|
| | | DOMMANAGERCHEKCAP_JSON(2)
|
| | | m_buf[m_buf_s++] = ']';
|
2026-07-19 15:56:41 -05:00 | | | }
|
| | |
|
2026-07-19 18:32:50 -05:00 | | | //---
|
2026-07-20 09:30:41 -05:00 | | | m_stack_it_obj_curr--; // Baja
|
| | | if(m_stack_it_obj_curr < 0)
|
2026-07-19 18:32:50 -05:00 | | | return true;
|
| | |
|
| | | //---
|
| | | next = 0; // break y vuleve a evaluar denbuvo...
|
2026-07-19 15:56:41 -05:00 | | | }
|
| | |
|
2026-07-19 18:32:50 -05:00 | | | //---
|
| | | return false;
|
2026-07-19 15:56:41 -05:00 | | | }
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-07-19 19:15:32 -05:00 | | | #endif // JSONPARSERBYLEO_SRC_JSONDOMSERIALIZER_MQH
|