ShemaJson/Src/Main.mqh

396 lines
11 KiB
MQL5
Raw Permalink Normal View History

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;
}
}
}
//---
}
//+------------------------------------------------------------------+