447 lines
12 KiB
MQL5
447 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| 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 <TSN\\BSFL\\Node.mqh>
| |
#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[JSONPARSERBYLEO_MAX_STACK];
| |
int m_stack_count[JSONPARSERBYLEO_MAX_STACK];
| |
int m_stack_ix[JSONPARSERBYLEO_MAX_STACK];
| |
int m_stack_k[JSONPARSERBYLEO_MAX_STACK];
| |
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
| |
static string out;
| |
m_stack_it_obj[m_stack_it_obj_curr].Key(out);
| |
| |
//---
| |
const int l = StringLen(out);
| |
const int r = ((l << 2) + (l << 1)) + 3;
| |
DOMMANAGERCHEKCAP_JSON(r)
| |
| |
//--- Escape
| |
m_buf[m_buf_s++] = '"';
| |
EscapeString(l, out);
| |
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
|