2026-06-22 16:51:02 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Main.mqh |
|
|
|
|
|
//| Copyright 2026, Niquel Mendoza. |
|
|
|
|
|
//| https://www.mql5.com/ |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "Copyright 2026, Niquel Mendoza."
|
|
|
|
|
#property link "https://www.mql5.com/"
|
|
|
|
|
#property strict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "Def.mqh"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
namespace TSN
|
|
|
|
|
{
|
|
|
|
|
class CJsonShemaValidator
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
CJsonNode m_json_shema;
|
|
|
|
|
CJsonParser* m_json_parser;
|
|
|
|
|
JsonShemaFuncStrToType m_shema_type_to_t;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int m_arr_ttype_to_json_type[];
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
char m_stack_state[512];
|
|
|
|
|
int m_stack_nodes[512];
|
|
|
|
|
int m_stack_shema[512];
|
|
|
|
|
int m_stack_end[512];
|
2026-06-28 07:58:04 -05:00
|
|
|
int m_stack_start[512];
|
|
|
|
|
|
|
|
|
|
//---
|
2026-06-22 16:51:02 -05:00
|
|
|
int m_sc;
|
|
|
|
|
int m_stack_size;
|
|
|
|
|
|
2026-06-28 07:58:04 -05:00
|
|
|
//
|
|
|
|
|
uchar m_next;
|
2026-06-22 16:51:02 -05:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CJsonShemaValidator(void);
|
|
|
|
|
~CJsonShemaValidator(void);
|
|
|
|
|
|
|
|
|
|
//---
|
2026-06-28 07:58:04 -05:00
|
|
|
void SetShema(CJsonNode& node);
|
|
|
|
|
void SetValid(CJsonParser* parser);
|
|
|
|
|
bool Validate();
|
2026-06-22 16:51:02 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CJsonShemaValidator::Validate()
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
while(true)
|
|
|
|
|
{
|
|
|
|
|
switch(m_next) // Tipo del json
|
|
|
|
|
{
|
|
|
|
|
case JSON_VTYPE_INTEGER:
|
|
|
|
|
{
|
2026-06-28 07:58:04 -05:00
|
|
|
CJsonNode current_shema = CJsonNode(m_json_parser, m_stack_shema[m_sc]);
|
2026-06-22 16:51:02 -05:00
|
|
|
const long value = node.m_ctx.m_cinta[m_stack_nodes[m_s] + 1];
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(current_shema.HasKey("const"))
|
|
|
|
|
{
|
|
|
|
|
CJsonNode v = current_shema["const"];
|
|
|
|
|
if(v.GetType() != JSON_VTYPE_INTEGER)
|
|
|
|
|
return false;
|
|
|
|
|
if(v.ToInt(LONG_MAX) != value)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
CJsonIteratorObj obj = current_shema.BeginObj();
|
|
|
|
|
while(obj.IsValid())
|
|
|
|
|
{
|
|
|
|
|
const int prop;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
switch(prop)
|
|
|
|
|
{
|
|
|
|
|
case JSONSHEMA_NUMBER_MIN:
|
|
|
|
|
{
|
|
|
|
|
const long prop_val = obj.Val().ToInt(0);
|
|
|
|
|
if(value < prop_val)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSONSHEMA_NUMBER_MAX:
|
|
|
|
|
{
|
|
|
|
|
const long prop_val = obj.Val().ToInt(0);
|
|
|
|
|
if(value > prop_val)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSONSHEMA_NUMBER_EX_MIN:
|
|
|
|
|
{
|
|
|
|
|
const long prop_val = obj.Val().ToInt(0);
|
|
|
|
|
if(value <= prop_val)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSONSHEMA_NUMBER_EX_MAX:
|
|
|
|
|
{
|
|
|
|
|
const long prop_val = obj.Val().ToInt(0);
|
|
|
|
|
if(value >= prop_val)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSONSHEMA_NUMBER_MULTIPLE:
|
|
|
|
|
{
|
|
|
|
|
const long prop_val = obj.Val().ToInt(0);
|
|
|
|
|
if((value % prop_val) != 0)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSONSHEMA_NUMBER_ENUM:
|
|
|
|
|
{
|
|
|
|
|
if(obj.Val().ExistInArray(value)) // Revisar
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
obj.Next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_stack_nodes[m_sc] += 2; // Salta int
|
|
|
|
|
m_stack_shema[m_sc] = current_shema.m_end;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSON_VTYPE_REAL:
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSON_VTYPE_BOOLEAN:
|
|
|
|
|
{
|
2026-06-28 07:58:04 -05:00
|
|
|
CJsonNode current_shema = CJsonNode(m_json_parser, m_stack_shema[m_sc]);
|
|
|
|
|
const bool value = bool((node.m_ctx.m_cinta[m_stack_nodes[m_s]] >> TSN_JSON_BIT_START_ENDTYPE) & 1);
|
2026-06-22 16:51:02 -05:00
|
|
|
|
2026-06-28 07:58:04 -05:00
|
|
|
if(current_shema.HasKey("const"))
|
|
|
|
|
{
|
|
|
|
|
CJsonNode v = current_shema["const"];
|
|
|
|
|
if(v.GetType() != JSON_VTYPE_BOOLEAN)
|
|
|
|
|
return false;
|
|
|
|
|
if(v.ToBool(false) != value)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if(current_shema.HasKey("enum"))
|
|
|
|
|
{
|
|
|
|
|
// Enum
|
|
|
|
|
CJsonNode v = current_shema["enum"];
|
|
|
|
|
if(v.ExistInArray(JSON_VTYPE_BOOLEANL, value))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_stack_nodes[m_sc] += 1; // Salta bool
|
|
|
|
|
m_stack_shema[m_sc] = current_shema.m_end;
|
|
|
|
|
|
|
|
|
|
//---
|
2026-06-22 16:51:02 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSON_VTYPE_STRING:
|
|
|
|
|
{
|
2026-06-28 07:58:04 -05:00
|
|
|
CJsonNode current_shema = CJsonNode(m_json_parser, m_stack_shema[m_sc]);
|
2026-06-22 16:51:02 -05:00
|
|
|
CJsonNode str = CJsonNode(node.m_ctx, m_stack_nodes[m_s]);
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const string value = str.ToString("");
|
|
|
|
|
|
2026-06-28 07:58:04 -05:00
|
|
|
if(current_shema.HasKey("const"))
|
2026-06-22 16:51:02 -05:00
|
|
|
{
|
2026-06-28 07:58:04 -05:00
|
|
|
CJsonNode v = current_shema["const"];
|
|
|
|
|
if(v.GetType() != JSON_VTYPE_STRING)
|
|
|
|
|
return false;
|
|
|
|
|
if(!v.CompareStrWith(value))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const int len = str.StrLen();
|
2026-06-22 16:51:02 -05:00
|
|
|
|
2026-06-28 07:58:04 -05:00
|
|
|
//---
|
|
|
|
|
CJsonIteratorObj obj = current_shema.BeginObj();
|
|
|
|
|
while(obj.IsValid())
|
2026-06-22 16:51:02 -05:00
|
|
|
{
|
2026-06-28 07:58:04 -05:00
|
|
|
const int prop =; // hash
|
|
|
|
|
switch(prop)
|
2026-06-22 16:51:02 -05:00
|
|
|
{
|
2026-06-28 07:58:04 -05:00
|
|
|
case JSONSHEMA_STR_MIN_LEN:
|
|
|
|
|
{
|
|
|
|
|
const long prop_val = obj.Val().ToInt(0);
|
|
|
|
|
if(len < prop_val)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSONSHEMA_STR_MAX_LEN:
|
|
|
|
|
{
|
|
|
|
|
const long prop_val = obj.Val().ToInt(0);
|
|
|
|
|
if(len > prop_val)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSONSHEMA_STR_PATTERN:
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSONSHEMA_STR_ENUM:
|
|
|
|
|
{
|
|
|
|
|
if(!obj.Val().ExistInArrayStr(value))
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
2026-06-22 16:51:02 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-28 07:58:04 -05:00
|
|
|
//---
|
|
|
|
|
obj.Next();
|
|
|
|
|
}
|
2026-06-22 16:51:02 -05:00
|
|
|
}
|
|
|
|
|
|
2026-06-28 07:58:04 -05:00
|
|
|
//---
|
|
|
|
|
m_stack_nodes[m_sc] += 1; // Salta string
|
|
|
|
|
m_stack_shema[m_sc] = current_shema.m_end;
|
2026-06-22 16:51:02 -05:00
|
|
|
|
2026-06-28 07:58:04 -05:00
|
|
|
//---
|
|
|
|
|
m_next = TSN_JSON_SHEMA_NEXT_EL;
|
2026-06-22 16:51:02 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSON_VTYPE_NULL:
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSON_VTYPE_ARR:
|
|
|
|
|
{
|
2026-06-28 07:58:04 -05:00
|
|
|
CJsonNode current_shema = CJsonNode(m_json_parser, m_stack_shema[m_sc]);
|
|
|
|
|
CJsonNode node = CJsonNode(m_json_parser, m_stack_nodes[m_sc]);
|
|
|
|
|
|
|
|
|
|
//--- Check tamaño
|
|
|
|
|
const int min = current_shema["minItems"].ToInt(INT_MIN);
|
|
|
|
|
const int max = current_shema["maxItems "].ToInt(INT_MAX);
|
|
|
|
|
const int ns = node.Size();
|
|
|
|
|
if(ns < min || ns > max)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
|
2026-06-22 16:51:02 -05:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case JSON_VTYPE_OBJ:
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
CJsonNode current_shema = CJsonNode(m_json_parser, m_stack_shema[m_sc]);
|
|
|
|
|
CJsonNode node = CJsonNode(m_json_parser, m_stack_nodes[m_sc]);
|
|
|
|
|
|
|
|
|
|
//--- Check tamaño
|
|
|
|
|
const int min = current_shema["minProperties"].ToInt(INT_MIN);
|
|
|
|
|
const int max = current_shema["maxProperties"].ToInt(INT_MAX);
|
|
|
|
|
const int ns = node.Size();
|
|
|
|
|
if(ns < min || ns > max)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//--- Verificamos required solo si no se permiten campos extra
|
|
|
|
|
if(!current_shema["additionalProperties"].ToBool(true))
|
|
|
|
|
{
|
|
|
|
|
CJsonNode required = current_shema["required"];
|
|
|
|
|
if(required.IsValid() && required.Size() > 0)
|
|
|
|
|
{
|
|
|
|
|
string keys[];
|
|
|
|
|
const int t = required.ToArray(keys);
|
|
|
|
|
if(!node.HasKeys(keys))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//--- Propiedades
|
|
|
|
|
CJsonNode props = current_shema["properties"];
|
|
|
|
|
if(props.Size() > 0)
|
|
|
|
|
{
|
|
|
|
|
m_sc++; // Abre pila...
|
|
|
|
|
m_stack_nodes[m_sc] = node.m_idx + 2 + 1; // Tambien a valor..
|
|
|
|
|
m_stack_shema[m_sc] = props.m_idx + 2 + 1 ; // ddesde objeto avanzamos a key y de key a valor..
|
|
|
|
|
m_stack_end[m_sc] = props.m_end; // Final del shema actual..
|
2026-06-28 07:58:04 -05:00
|
|
|
m_stack_state[m_sc] = TSN_JSON_CTX_IN_OBJ;
|
2026-06-22 16:51:02 -05:00
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
// QUIZAS revisemos esto
|
|
|
|
|
|
|
|
|
|
if(m_stack_end[np] > m_stack_shema[np])
|
|
|
|
|
{
|
|
|
|
|
m_next = ; // Caluclar hash de type
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_next = TSN_JSON_SHEMA_POP;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--- Saltar objeto vacio
|
|
|
|
|
m_stack_nodes[m_sc] = node.m_end;
|
|
|
|
|
m_stack_shema[m_sc] = current_shema.m_end;
|
|
|
|
|
// Ahora mismo shema en la sigeuinte key
|
|
|
|
|
// node en el sigueite key
|
|
|
|
|
m_next = TSN_JSON_SHEMA_NEXT_EL;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TSN_JSON_SHEMA_NEXT_EL:
|
|
|
|
|
{
|
|
|
|
|
// Aca igua revisar si estasmo validando items de array o iterms de obj (cambia cuanto avanzamos..)
|
2026-06-28 07:58:04 -05:00
|
|
|
// Ahoira mismo apuntamos a key
|
|
|
|
|
if(m_stack_state[m_sc] == TSN_JSON_CTX_IN_OBJ)
|
2026-06-22 16:51:02 -05:00
|
|
|
{
|
2026-06-28 07:58:04 -05:00
|
|
|
//---
|
|
|
|
|
// Ahora mismo ambos en keys a que ver si son las mismas...
|
|
|
|
|
if(!m_json_parser.KeysCompare(m_stack_shema[m_sc], m_stack_nodes[m_sc]))
|
|
|
|
|
return false;
|
2026-06-22 16:51:02 -05:00
|
|
|
|
2026-06-28 07:58:04 -05:00
|
|
|
//---
|
|
|
|
|
m_stack_shema[m_sc]++; // 1 key y otro para valor
|
|
|
|
|
if(m_stack_shema[m_sc] >= m_stack_end[np]) // Supero en shema
|
|
|
|
|
{
|
|
|
|
|
m_next = TSN_JSON_SHEMA_POP; // Pop al nivel actual...
|
|
|
|
|
// Dado que ya no hay mas valores..
|
|
|
|
|
}
|
2026-06-22 16:51:02 -05:00
|
|
|
|
2026-06-28 07:58:04 -05:00
|
|
|
//---
|
|
|
|
|
if(int(m_json_parser.m_cinta[m_stack_shema[m_sc]] & 0xF) != JSON_VTYPE_OBJ) // Error deberiamos de stare en obj
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_stack_nodes[m_sc]++; // 1 de key y otro para apuntar a valor
|
|
|
|
|
const int t = int(m_json_parser.m_cinta[m_stack_nodes[m_sc]] & 0xF);
|
|
|
|
|
if(((1 << t)&JSONVTYPE_FLAG_KEY) != 0) // Error deberiamos de stare en un valor..
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_next = ; // Caluclar hash de type
|
|
|
|
|
}
|
|
|
|
|
else // Arr
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
2026-06-22 16:51:02 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TSN_JSON_SHEMA_POP:
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
m_stack_size = m_sc--;
|
|
|
|
|
if(m_stack_size < 1) // Fin..
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_next = TSN_JSON_SHEMA_NEXT_EL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|