JsonParserByLeo/Src/JsonBuilder.mqh
Nique_372 7a1380a824
2026-06-03 22:08:03 -05:00

406 lines
11 KiB
MQL5

//+------------------------------------------------------------------+
//| JsonBuilder.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_JSONBUILDER_MQH
#define JSONPARSERBYLEO_SRC_JSONBUILDER_MQH
namespace TSN
{
//+------------------------------------------------------------------+
//| Escape lookup tables (index = char code 0..127) |
//| g_arr_jsonb_type_c: 0 = safe (copy as-is) |
//| 1 = two-char escape: backslash + g_arr_jsonb_next_c[c] |
//| 2 = \u00XX hex escape |
//+------------------------------------------------------------------+
const uchar g_arr_jsonb_type_c[128] =
{
2, 2, 2, 2, 2, 2, 2, 2, // 0x00-0x07
1, 1, 1, 2, 1, 1, 2, 2, // 0x08-0x0F (\b=8,\t=9,\n=10, \f=12,\r=13)
2, 2, 2, 2, 2, 2, 2, 2, // 0x10-0x17
2, 2, 2, 2, 2, 2, 2, 2, // 0x18-0x1F
0, 0, 1, 0, 0, 0, 0, 0, // 0x20-0x27 ('"'=34)
0, 0, 0, 0, 0, 0, 0, 0, // 0x28-0x2F
0, 0, 0, 0, 0, 0, 0, 0, // 0x30-0x37
0, 0, 0, 0, 0, 0, 0, 0, // 0x38-0x3F
0, 0, 0, 0, 0, 0, 0, 0, // 0x40-0x47
0, 0, 0, 0, 0, 0, 0, 0, // 0x48-0x4F
0, 0, 0, 0, 0, 0, 0, 0, // 0x50-0x57
0, 0, 0, 0, 1, 0, 0, 0, // 0x58-0x5F ('\\'=92)
0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x67
0, 0, 0, 0, 0, 0, 0, 0, // 0x68-0x6F
0, 0, 0, 0, 0, 0, 0, 0, // 0x70-0x77
0, 0, 0, 0, 0, 0, 0, 0 // 0x78-0x7F
};
//--- Second char of two-char escape (only valid where g_arr_jsonb_type_c==1)
const uchar g_arr_jsonb_next_c[128] =
{
0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x07
'b', 't', 'n', 0, 'f', 'r', 0, 0, // 0x08-0x0F
0, 0, 0, 0, 0, 0, 0, 0, // 0x10-0x17
0, 0, 0, 0, 0, 0, 0, 0, // 0x18-0x1F
0, 0, '"', 0, 0, 0, 0, 0, // 0x20-0x27
0, 0, 0, 0, 0, 0, 0, 0, // 0x28-0x2F
0, 0, 0, 0, 0, 0, 0, 0, // 0x30-0x37
0, 0, 0, 0, 0, 0, 0, 0, // 0x38-0x3F
0, 0, 0, 0, 0, 0, 0, 0, // 0x40-0x47
0, 0, 0, 0, 0, 0, 0, 0, // 0x48-0x4F
0, 0, 0, 0, 0, 0, 0, 0, // 0x50-0x57
0, 0, 0, 0, '\\', 0, 0, 0, // 0x58-0x5F
0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x67
0, 0, 0, 0, 0, 0, 0, 0, // 0x68-0x6F
0, 0, 0, 0, 0, 0, 0, 0, // 0x70-0x77
0, 0, 0, 0, 0, 0, 0, 0 // 0x78-0x7F
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define JSONBUILDER_EXPAND(need) if(m_pos + (need) >= m_cap) { m_cap += (need) + 4096; ArrayResize(m_buf, m_cap, m_cap); }
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CJsonBuilder
{
public:
uchar m_buf[];
int m_pos;
private:
int m_cap;
bool m_stack_first[64];
int m_sp;
//---
void PutRaw(const string& s);
void PutRaw(const uchar& s[]);
void PutRawNoRef(const string s);
void PutEncodedStr(const string& s);
public:
CJsonBuilder(int initial_cap = 1024);
~CJsonBuilder() {}
//---
void Clear();
//---
__forceinline string Build() const { return CharArrayToString(m_buf, 0, m_pos, CP_UTF8); }
//--- Structure
CJsonBuilder* Obj();
CJsonBuilder* EndObj();
CJsonBuilder* Arr();
CJsonBuilder* EndArr();
//--- Key / Values
CJsonBuilder* Key(const string& k);
CJsonBuilder* ValS(const string& v);
CJsonBuilder* ValSNoRef(const string v);
CJsonBuilder* Val(int v);
CJsonBuilder* Val(long v);
CJsonBuilder* Val(double v);
CJsonBuilder* Val(bool v);
CJsonBuilder* ValidJson(const uchar& v[]);
CJsonBuilder* ValidJson(const string& fragment);
CJsonBuilder* ValidJsonNoRef(const string fragment);
CJsonBuilder* Null();
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CJsonBuilder::CJsonBuilder(int initial_cap)
{
m_cap = initial_cap;
ArrayResize(m_buf, m_cap);
m_pos = 0;
m_sp = 0;
m_stack_first[0] = true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define JSON_BUILDER_PUT(c) \
JSONBUILDER_EXPAND(1) \
m_buf[m_pos++] = c; \
//---
#define JSON_BUILDER_COMMA \
if(!m_stack_first[m_sp]) \
{ \
JSON_BUILDER_PUT(',') \
} \
m_stack_first[m_sp] = false;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CJsonBuilder::Clear()
{
m_pos = 0;
m_sp = 0;
m_stack_first[0] = true;
}
//+------------------------------------------------------------------+
void CJsonBuilder::PutRaw(const uchar& s[])
{
//---
const int l = ArraySize(s);
const int max_bytes = l * 4 + 1;
JSONBUILDER_EXPAND(max_bytes)
//---
m_pos += ArrayCopy(m_buf, s, 0, m_pos, WHOLE_ARRAY);
}
//+------------------------------------------------------------------+
void CJsonBuilder::PutRaw(const string& s)
{
//---
const int l = StringLen(s);
const int max_bytes = l * 4 + 1;
JSONBUILDER_EXPAND(max_bytes)
//---
int written = StringToCharArray(s, m_buf, m_pos, WHOLE_ARRAY, CP_UTF8);
if(written > 0 && m_buf[m_pos + written - 1] == 0)
written--;
m_pos += written;
}
//+------------------------------------------------------------------+
void CJsonBuilder::PutRawNoRef(const string s)
{
//---
const int l = StringLen(s);
const int max_bytes = l * 4 + 1;
JSONBUILDER_EXPAND(max_bytes)
//---
int written = StringToCharArray(s, m_buf, m_pos, WHOLE_ARRAY, CP_UTF8);
if(written > 0 && m_buf[m_pos + written - 1] == 0)
written--;
m_pos += written;
}
//+------------------------------------------------------------------+
void CJsonBuilder::PutEncodedStr(const string& s)
{
//---
JSON_BUILDER_PUT('"')
//---
const int l = StringLen(s);
//---
for(int i = 0; i < l; i++)
{
//---
const ushort c = s[i];
//---
if(c >= 128)
{
//--- UTF-8 multibyte
uchar temp[];
int tlen = StringToCharArray(ShortToString(c), temp, 0, WHOLE_ARRAY, CP_UTF8);
if(tlen > 0 && temp[tlen - 1] == 0)
tlen--;
JSONBUILDER_EXPAND(tlen)
ArrayCopy(m_buf, temp, m_pos, 0, tlen);
m_pos += tlen;
continue;
}
//--- ASCII lookup
const uchar esc = g_arr_jsonb_type_c[c];
if(esc == 0)
{
JSONBUILDER_EXPAND(1)
m_buf[m_pos++] = (uchar)c;
}
else
if(esc == 1)
{
JSONBUILDER_EXPAND(2)
m_buf[m_pos++] = '\\';
m_buf[m_pos++] = g_arr_jsonb_next_c[c];
}
else // esc == 2: \u00XX
{
JSONBUILDER_EXPAND(6)
m_buf[m_pos++] = '\\';
m_buf[m_pos++] = 'u';
m_buf[m_pos++] = '0';
m_buf[m_pos++] = '0';
//---
uchar t = uchar((c >> 4) & 0xF);
m_buf[m_pos++] = (uchar)(t < 10 ? '0' + t : 'a' + t - 10);
t = uchar(c & 0xF);
m_buf[m_pos++] = (uchar)(t < 10 ? '0' + t : 'a' + t - 10);
}
}
//---
JSON_BUILDER_PUT('"')
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::Obj()
{
JSON_BUILDER_COMMA
JSON_BUILDER_PUT('{')
if(m_sp < 63)
{
m_sp++;
m_stack_first[m_sp] = true;
}
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::EndObj()
{
if(m_sp > 0)
m_sp--;
JSON_BUILDER_PUT('}')
m_stack_first[m_sp] = false;
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::Arr()
{
JSON_BUILDER_COMMA
JSON_BUILDER_PUT('[')
if(m_sp < 63)
{
m_sp++;
m_stack_first[m_sp] = true;
}
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::EndArr()
{
if(m_sp > 0)
m_sp--;
JSON_BUILDER_PUT(']')
m_stack_first[m_sp] = false;
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::Key(const string& k)
{
JSON_BUILDER_COMMA
PutEncodedStr(k);
JSON_BUILDER_PUT(':')
m_stack_first[m_sp] = true;
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::ValS(const string& v)
{
JSON_BUILDER_COMMA
PutEncodedStr(v);
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::ValSNoRef(const string v)
{
JSON_BUILDER_COMMA
PutEncodedStr(v);
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::Val(int v)
{
JSON_BUILDER_COMMA
PutRawNoRef((string)v);
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::Val(long v)
{
JSON_BUILDER_COMMA
PutRawNoRef((string)v);
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::Val(double v)
{
JSON_BUILDER_COMMA
PutRawNoRef((string)v);
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::Val(bool v)
{
JSON_BUILDER_COMMA
PutRawNoRef(v ? "true" : "false");
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::ValidJson(const uchar& v[])
{
JSON_BUILDER_COMMA
PutRaw(v);
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::ValidJson(const string& fragment)
{
JSON_BUILDER_COMMA
PutRaw(fragment);
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::ValidJsonNoRef(const string fragment)
{
JSON_BUILDER_COMMA
PutRaw(fragment);
return &this;
}
//+------------------------------------------------------------------+
CJsonBuilder* CJsonBuilder::Null()
{
JSON_BUILDER_COMMA
PutRaw("null");
return &this;
}
//---
CJsonBuilder g_json_builder;
} // namespace TSN
//+------------------------------------------------------------------+
#endif // JSONPARSERBYLEO_SRC_JSONBUILDER_MQH