JsonParserByLeo/Src/JsonNode.mqh
Nique_372 92e074d65d
2026-10-03 17:00:34 -05:00

481 lines
17 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 "JsonUnescape.mqh"
#include "JsonDomSerializer.mqh"
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
namespace TSN
{
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// Layour de los demas en repo base
// Layout cinta:
// NULL 1 pos [tipo(4)]
// Array y obj tambien tine un slot mas (para el stirng raw..)
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define CJsonIteratorArray TSN::CNodeBaseIteradorArray<TSN::CJsonParser, TSN::CJsonNode>
#define CJsonIteratorArrayFast TSN::CNodeBaseIteradorArrayFast<TSN::CJsonParser, TSN::CJsonNode>
#define CJsonIteratorObj TSN::CNodeBaseIteratorObj<TSN::CJsonParser, TSN::CJsonNode>
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
struct CJsonNode : CNodeSFLBase<CJsonParser, ENUM_JSON_VTYPE, CJsonNode>
{
public:
//---
CJsonNode()
: CNodeSFLBase<CJsonParser, ENUM_JSON_VTYPE, CJsonNode>(NULL, -1, -1, -1) {}
//--- Resuelve m_node_pos una unica vez aqui
CJsonNode(CJsonParser* _ctx, const int _idx, const int _end)
: CNodeSFLBase<CJsonParser, ENUM_JSON_VTYPE, CJsonNode>(_ctx, _idx, _end, NODEBASEF_RES_BASE(_ctx, _idx)) {}
CJsonNode(CJsonParser* _ctx, const int _idx)
: CNodeSFLBase<CJsonParser, ENUM_JSON_VTYPE, CJsonNode>(_ctx, _idx, _idx + _ctx.GetStep(_idx), NODEBASEF_RES_BASE(_ctx, _idx)) {}
// Exclusivo de copia (Aqui NO RESUELVE NADA NI GET STE NI NADA) (constructor de clonacion)
CJsonNode(CJsonParser* _ctx, const int _idx, const int _end, const int _node_pos)
: CNodeSFLBase<CJsonParser, ENUM_JSON_VTYPE, CJsonNode>(_ctx, _idx, _end, _node_pos) {}
//--- Acceso
CJsonNode operator[](const string& key);
//--- String
string ToString(const string def = "") const;
__forceinline int ToStringUArray(uchar& data[]) const;
__forceinline string ToStringNoEscape(const string def = "", uint cp = CP_UTF8) const;
__forceinline int ToStringUArrayNoEscape(uchar& data[]) const;
__forceinline int StrLenRaw() const;
__forceinline int StrStart() const;
//---
string ComplexToString(bool clean, uint cp = CP_ACP) const;
int ComplexToData(uchar& data[], bool clean, int dst_start = 0, bool ajustar_arr = true) const;
//---
//- UInt
long ToUInt(const ulong def) const;
bool ToUIntSafe(ulong& val) const;
//- Int
long ToInt(const long def) const;
bool ToIntSafe(long& val) const;
//- Double
double ToDouble(const double def) const;
bool ToDoubleSafe(double& val) const;
//--- MqlParam
bool ToMqlParam(MqlParam& param, const ENUM_DATATYPE type) const;
//--- Is
__forceinline bool IsNull() const;
//--- Navegacion raw
static const CJsonNode EMPTY_NODE;
static void OnMute(CNodeSFLBase<CJsonParser, ENUM_JSON_VTYPE, CJsonNode>& node);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
const CJsonNode CJsonNode::EMPTY_NODE;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
static void CJsonNode::OnMute(CNodeSFLBase<CJsonParser, ENUM_JSON_VTYPE, CJsonNode> &node)
{
node.RecalcNodePos(); // Posicion...
node.m_end = node.m_idx + node.m_ctx.GetStep(node.m_idx); // Calculamos el fin...
}
//+------------------------------------------------------------------+
//| To MqlParam |
//+------------------------------------------------------------------+
bool CJsonNode::ToMqlParam(MqlParam& param, const ENUM_DATATYPE type) const
{
//---
// Si no es un tipo de data valido salimos
if(NODEBASE_IS_NOT_VALID_F ||
CJsonParser::s_dtype_to_str[uchar(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE)].Length() == 0)
return false;
//---
param.type = type;
//---
switch(type)
{
case TYPE_BOOL:
param.integer_value = ToBool(false);
break;
case TYPE_CHAR:
param.integer_value = char(ToInt(0));
break;
case TYPE_UCHAR:
param.integer_value = uchar(ToInt(0));
break;
case TYPE_SHORT:
param.integer_value = short(ToInt(0));
break;
case TYPE_USHORT:
param.integer_value = ushort(ToInt(0));
break;
case TYPE_COLOR:
param.integer_value = long(color(ToString("")));
break;
case TYPE_INT:
param.integer_value = int(ToInt(0));
break;
case TYPE_UINT:
param.integer_value = uint(ToInt(0));
break;
case TYPE_DATETIME:
param.integer_value = long(datetime(ToString("")));
break;
case TYPE_LONG:
case TYPE_ULONG:
param.integer_value = ToInt(0);
break;
case TYPE_FLOAT:
param.double_value = float(ToInt(0));
break;
case TYPE_DOUBLE:
param.double_value = double(ToInt(0));
break;
case TYPE_STRING:
param.string_value = ToString("");
break;
default:
param.string_value = ToString("");
break;
}
//---
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
string CJsonNode::ComplexToString(bool clean, uint cp = CP_ACP) const
{
if(NODEBASE_IS_NOT_VALID_F || ((1 << int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE)) & (TSN_SBL_BASE_F_OBJ | TSN_SBL_BASE_F_ARR)) == 0)
return "";
//---
const int start = int(m_ctx.m_cinta[m_idx + 1] & TSN_JSON_BIT_RAW_MASK_S);
const int end = int(m_ctx.m_cinta[m_idx + 1] >> TSN_JSON_BIT_END_T);
return clean ? m_ctx.CleanNode(start, end) : CharArrayToString(m_ctx.m_raw, start, (end - start) + 1, cp);
}
//+------------------------------------------------------------------+
int CJsonNode::ComplexToData(uchar &data[], bool clean, int dst_start = 0, bool ajustar_arr = true) const
{
if(NODEBASE_IS_NOT_VALID_F || ((1 << int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE)) & (TSN_SBL_BASE_F_OBJ | TSN_SBL_BASE_F_ARR)) == 0)
return 0;
//---
const int start = int(m_ctx.m_cinta[m_idx + 1] & TSN_JSON_BIT_RAW_MASK_S);
const int end = int(m_ctx.m_cinta[m_idx + 1] >> TSN_JSON_BIT_END_T);
return clean ? m_ctx.CleanNode(start, end, data, dst_start, ajustar_arr) :
ArrayCopy(data, m_ctx.m_raw, dst_start, start, (end - start) + 1);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CJsonNode CJsonNode::operator[](const string& key)
{
//---
if(NODEBASE_IS_NOT_VALID_F || int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE) != JSON_VTYPE_OBJ)
return EMPTY_NODE;
//--- primero vemos si hay o no hash
if(m_node_pos != -1)
{
const int pos = m_ctx.PerfectHashComputeHash(key, m_node_pos);
return pos == -1 ? EMPTY_NODE : CJsonNode(m_ctx, pos + 1);
}
//--- No esta hasheado..
//--- Verificamos si vale la pena hashear este nodo o no
const long v = m_ctx.m_cinta[m_idx];
const int num_el = int((v >> TSN_SBL_BIT_START_NUM_EL) & TSN_SBL_BIT_MASK_NUM_EL);
if(num_el >= TSN_NODEBASEREAD_MIN_JIT_VALUES_TO_HASH) // como minimo buscamores esos elementos
{
int c = int(v >> TSN_SBL_BIT_START_NUM_C_HASH) & TSN_SBL_BIT_MASK_NUM_C_HASH;
if(c >= TSN_NODEBASEREAD_MIN_JIT_ATTEMPS_TO_PHASH) // Veriricamos si supero
{
if(m_ctx.NodeHashing(m_idx, num_el, m_end, m_node_pos))
{
m_ctx.m_cinta[m_idx] |= (1LL << TSN_SBL_BIT_START_IS_HASING);
const int pos = m_ctx.PerfectHashComputeHash(key, m_node_pos);
return pos == -1 ? EMPTY_NODE : CJsonNode(m_ctx, pos + 1);
}
c = 0;
}
else
c++;
//---
m_ctx.m_cinta[m_idx] = (v & TSN_SBL_CCLEAN_MASK_JIT_TRY) | (c << TSN_SBL_BIT_START_NUM_C_HASH);
}
//--- En caso aun estemos porbando o no vale la pena.. entonces lineal..
int cur = m_idx + 2;
while(cur < m_end)
{
if(int(m_ctx.m_cinta[cur] & TSN_SBL_BIT_MASK_TYPE) != JSON_VTYPE_KEY)
{
cur += m_ctx.GetStep(cur);
continue;
}
if(m_ctx.KeysEqual(key, cur))
return CJsonNode(m_ctx, cur + 1);
//---
cur++; // salta KEY
cur += m_ctx.GetStep(cur); // salta VAL
}
//---
return EMPTY_NODE;
}
//+------------------------------------------------------------------+
//| String |
//+------------------------------------------------------------------+
string CJsonNode::ToString(const string def = "") const
{
//---
if(NODEBASE_IS_NOT_VALID_F)
return def;
//---
switch(int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE))
{
case JSON_VTYPE_UINTEGER:
return CValueToBuffer::ULLToStr(ulong(m_ctx.m_cinta[m_idx + 1]));
case JSON_VTYPE_INTEGER:
return string(m_ctx.m_cinta[m_idx + 1]);
case JSON_VTYPE_REAL:
{
TsnBitInterprete 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_SBL_BIT_START_ENDTYPE) & 1) != 0 ? "true" : "false";
case JSON_VTYPE_STRING:
{
static string res;
return CJsonParser::MakeString(m_ctx.m_raw, TSN_SBL_CNG_STR_START(m_idx), TSN_SBL_CNG_STR_LEN(m_idx), res) ? res : def;
}
default:
return def;
}
}
//+------------------------------------------------------------------+
__forceinline string CJsonNode::ToStringNoEscape(const string def = "", uint cp = CP_UTF8) const
{
//---
if(NODEBASE_IS_NOT_VALID_F || int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE) != JSON_VTYPE_STRING)
return def;
return CharArrayToString(m_ctx.m_raw, TSN_SBL_CNG_STR_START(m_idx), TSN_SBL_CNG_STR_LEN(m_idx), cp);
}
//+------------------------------------------------------------------+
// NOTA: funciones que trabajan con arrays retornan -1 en este caso como señal de falo.. no
// hay def posible aqui
__forceinline int CJsonNode::ToStringUArray(uchar & data[]) const
{
//---
if(NODEBASE_IS_NOT_VALID_F || int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE) != JSON_VTYPE_STRING)
return -1;
return CJsonParser::MakeUArray(m_ctx.m_raw, TSN_SBL_CNG_STR_START(m_idx), TSN_SBL_CNG_STR_LEN(m_idx), data);
}
//+------------------------------------------------------------------+
__forceinline int CJsonNode::ToStringUArrayNoEscape(uchar &data[]) const
{
//---
if(NODEBASE_IS_NOT_VALID_F || int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE) != JSON_VTYPE_STRING)
return -1;
return ArrayCopy(data, m_ctx.m_raw, 0, TSN_SBL_CNG_STR_START(m_idx), TSN_SBL_CNG_STR_LEN(m_idx));
}
//+------------------------------------------------------------------+
__forceinline int CJsonNode::StrLenRaw(void) const
{
//---
if(NODEBASE_IS_NOT_VALID_F || int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE) != JSON_VTYPE_STRING)
return 0;
return TSN_SBL_CNG_STR_LEN(m_idx);
}
//+------------------------------------------------------------------+
__forceinline int CJsonNode::StrStart(void) const
{
//---
if(NODEBASE_IS_NOT_VALID_F || int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE) != JSON_VTYPE_STRING)
return 0;
return TSN_SBL_CNG_STR_START(m_idx);
}
//+------------------------------------------------------------------+
//| ToInt |
//+------------------------------------------------------------------+
long CJsonNode::ToInt(const long def) const
{
//---
if(NODEBASE_IS_NOT_VALID_F)
return def;
//---
switch(int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE))
{
case TSN_SBL_BASE_TYPE_UINT:
case TSN_SBL_BASE_TYPE_INT:
return m_ctx.m_cinta[m_idx + 1];
case TSN_SBL_BASE_TYPE_FLT:
{
TsnBitInterprete un;
un.long_value = m_ctx.m_cinta[m_idx + 1];
return long(un.double_value);
}
case TSN_SBL_BASE_TYPE_BOL:
return long((m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_START_ENDTYPE) & 1);
case TSN_SBL_BASE_TYPE_STR:
return long(CharArrayToString(m_ctx.m_raw, TSN_SBL_CNG_STR_START(m_idx), TSN_SBL_CNG_STR_LEN(m_idx)));
default:
return def;
}
}
//+------------------------------------------------------------------+
bool CJsonNode::ToIntSafe(long & val) const
{
//---
if(NODEBASE_IS_NOT_VALID_F)
return false;
//---
switch(int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE))
{
case TSN_SBL_BASE_TYPE_UINT:
case TSN_SBL_BASE_TYPE_INT:
val = m_ctx.m_cinta[m_idx + 1];
return true;
case TSN_SBL_BASE_TYPE_FLT:
{
TsnBitInterprete un;
un.long_value = m_ctx.m_cinta[m_idx + 1];
val = long(un.double_value);
return true;
}
case TSN_SBL_BASE_TYPE_BOL:
val = long((m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_START_ENDTYPE) & 1);
return true;
case TSN_SBL_BASE_TYPE_STR:
val = long(CharArrayToString(m_ctx.m_raw, TSN_SBL_CNG_STR_START(m_idx), TSN_SBL_CNG_STR_LEN(m_idx)));
return true;
default:
return false;
}
}
//+------------------------------------------------------------------+
//| ToDouble |
//+------------------------------------------------------------------+
double CJsonNode::ToDouble(const double def) const
{
//---
if(NODEBASE_IS_NOT_VALID_F)
return def;
//---
switch(int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE))
{
case TSN_SBL_BASE_TYPE_UINT:
return double(ulong(m_ctx.m_cinta[m_idx + 1]));
case TSN_SBL_BASE_TYPE_INT:
return double(m_ctx.m_cinta[m_idx + 1]);
case TSN_SBL_BASE_TYPE_FLT:
{
TsnBitInterprete un;
un.long_value = m_ctx.m_cinta[m_idx + 1];
return un.double_value;
}
case TSN_SBL_BASE_TYPE_BOL:
return double((m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_START_ENDTYPE) & 1);
case TSN_SBL_BASE_TYPE_STR:
return double(CharArrayToString(m_ctx.m_raw, TSN_SBL_CNG_STR_START(m_idx), TSN_SBL_CNG_STR_LEN(m_idx)));
default:
return def;
}
}
//+------------------------------------------------------------------+
bool CJsonNode::ToDoubleSafe(double & val) const
{
//---
if(NODEBASE_IS_NOT_VALID_F)
return false;
//---
switch(int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE))
{
case TSN_SBL_BASE_TYPE_UINT:
val = double(ulong(m_ctx.m_cinta[m_idx + 1]));
return true;
case TSN_SBL_BASE_TYPE_INT:
val = double(m_ctx.m_cinta[m_idx + 1]);
return true;
case TSN_SBL_BASE_TYPE_FLT:
{
TsnBitInterprete un;
un.long_value = m_ctx.m_cinta[m_idx + 1];
val = un.double_value;
return true;
}
case TSN_SBL_BASE_TYPE_BOL:
val = double((m_ctx.m_cinta[m_idx] >> TSN_SBL_BIT_START_ENDTYPE) & 1);
return true;
case TSN_SBL_BASE_TYPE_STR:
val = double(CharArrayToString(m_ctx.m_raw, TSN_SBL_CNG_STR_START(m_idx), TSN_SBL_CNG_STR_LEN(m_idx)));
return true;
default:
return false;
}
}
//+------------------------------------------------------------------+
//| Is |
//+------------------------------------------------------------------+
__forceinline bool CJsonNode::IsNull() const
{
return NODEBASE_IS_VALID && int(m_ctx.m_cinta[m_idx] & TSN_SBL_BIT_MASK_TYPE) == JSON_VTYPE_NULL;
}
}
//+------------------------------------------------------------------+
#endif // JSONPARSERBYLEO_SRC_JSONNODE_MQH