//+------------------------------------------------------------------+ //| 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]; int m_stack_start[512]; //--- int m_sc; int m_stack_size; // uchar m_next; public: CJsonShemaValidator(void); ~CJsonShemaValidator(void); //--- void SetShema(CJsonNode& node); void SetValid(CJsonParser* parser); bool Validate(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CJsonShemaValidator::Validate() { //--- while(true) { switch(m_next) // Tipo del json { case JSON_VTYPE_INTEGER: { CJsonNode current_shema = CJsonNode(m_json_parser, m_stack_shema[m_sc]); 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: { 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); 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; //--- break; } case JSON_VTYPE_STRING: { CJsonNode current_shema = CJsonNode(m_json_parser, m_stack_shema[m_sc]); CJsonNode str = CJsonNode(node.m_ctx, m_stack_nodes[m_s]); //--- const string value = str.ToString(""); if(current_shema.HasKey("const")) { 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(); //--- CJsonIteratorObj obj = current_shema.BeginObj(); while(obj.IsValid()) { const int prop =; // hash switch(prop) { 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: return false; } //--- obj.Next(); } } //--- m_stack_nodes[m_sc] += 1; // Salta string m_stack_shema[m_sc] = current_shema.m_end; //--- m_next = TSN_JSON_SHEMA_NEXT_EL; break; } case JSON_VTYPE_NULL: { break; } case JSON_VTYPE_ARR: { 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; //--- 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.. m_stack_state[m_sc] = TSN_JSON_CTX_IN_OBJ; //--- // 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..) // Ahoira mismo apuntamos a key if(m_stack_state[m_sc] == TSN_JSON_CTX_IN_OBJ) { //--- // 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; //--- 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.. } //--- 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 { } 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; } } } //--- } //+------------------------------------------------------------------+