//+------------------------------------------------------------------+ //| JsonSaxImp.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_JSONSAXIMP_MQH #define JSONPARSERBYLEO_SRC_JSONSAXIMP_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "JsonParser.mqh" #include "JsonSax.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #define JSONPARSERBYLEO_SAX_STR_START (0) /* Notas de mi parte Siempre cuando vuevea a llamar aseungramtne de agregar datos amenos un solo dato es lo minimo.. */ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CJsonParserStream : public CJsonParser { private: ITsnJsonParserStream* m_in; bool m_del_caller; //--- uchar m_prev_c; uint8_t m_in_n; long m_mask; long m_last_number; double m_last_number_dbl; //---- long m_sax_frac_int; int m_sax_frac_digits; public: CJsonParserStream(void) : m_del_caller(false) {} ~CJsonParserStream(void); //--- __forceinline int CurrentStack() const { return m_stak_curr + 1; } __forceinline int CurrentStackState() const { return m_stak_curr_state; } void KillParse(); //--- void SetCaller(ITsnJsonParserStream* caller, bool del); void CheckLimpieza(); //--- bool ParseSreamSax(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CJsonParserStream::~CJsonParserStream(void) { if(m_del_caller && CheckPointer(m_in) == POINTER_DYNAMIC) delete m_in; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Al terminar solo lo poneres en tok invalid.. #define JSONPARSERBYLEO_SAX_ON_FINISH m_next=TSN_JSON_TOK_INVALID; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CJsonParserStream::KillParse(void) { JSONPARSERBYLEO_SAX_ON_FINISH } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CJsonParserStream::SetCaller(ITsnJsonParserStream *caller, bool del) { m_del_caller = del; m_in = caller; m_in.MainPointer(&this); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CJsonParserStream::CheckLimpieza(void) { // Revisamos el estado previo // La idea ees ver si estamos en string SI no lo estabamos entonces es seguro retrazar el pos if(((1 << m_next) & (TSN_JSON_F_KEY | TSN_JSON_F_STRING)) == 0) { m_pos = 0; // Reiniciamos el pos // Entonces la idea es que la siguiente llama el que llame a esta clase // Llente el array auqneusea conreserva pero hasta m_len // Lo deal seria ejeuctar esto antes de cada relleneado } // No se puede.. } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CJsonParserStream::ParseSreamSax(void) { //--- Inicial if(m_next == TSN_JSON_TOK_INVALID) { // general m_stak_curr = -1; m_last_err = TSN_JSON_NOT_ERR; m_pos = m_offset; // numeros m_last_number = 0; m_mask = 0; //dbl m_in_n = 0; // extra m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] = -1; // string y keys //--- m_in.OnStartDocument(); //--- Inicial const uchar _f_c = m_raw[m_pos]; if(_f_c == '{') { m_next = TSN_JSON_TOK_LLAVE_INI; } else if(_f_c == '[') { m_next = TSN_JSON_TOK_COR_INI; } else { m_pos++; while(true) { const uchar _f__c = m_raw[m_pos]; if(_f__c < 33) { m_pos++; continue; } //--- if(_f__c == '{') { m_next = TSN_JSON_TOK_LLAVE_INI; } else if(_f__c == '[') { m_next = TSN_JSON_TOK_COR_INI; } else { m_next = TSN_JSON_TOK_INVALID; } break; } } } //--- Bucle while(true) { switch(m_next) { case TSN_JSON_TOK_NUMBER_NEG: { //--- // m_last_number=0 ya viene a 0 m_mask = -1; // solo cambiamos la mascara m_next = TSN_JSON_TOK_NUMBER; //--- if(++m_pos >= m_len) // Ahora en numero de lo contraio a la siguiente.. return true; break; } //--- case TSN_JSON_TOK_NUMBER: { //--- uchar ch = m_raw[m_pos] ^ '0'; //--- bucle principal while(true) { m_last_number = (m_last_number << 3) + (m_last_number << 1) + ch; m_pos++; if(m_pos >= m_len) return true; ch = m_raw[m_pos] ^ '0'; if(ch < 10) continue; else if(ch == 30 || (ch | 0x20) == 117) { m_prev_c = ch; m_last_number_dbl = double(m_last_number); m_next = TSN_JSON_TOK_SAX_DBL; // dbl ya reincia .. break; } else { m_in.OnInteger((m_last_number ^ m_mask) - m_mask); m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; // reiniciamos m_last_number = 0; m_mask = 0; break; } } //--- no hago el m_pos++ dado que ahora mismo estamo en el seg char break; } //--- case TSN_JSON_TOK_STRING: { //--- //TSN_JSON_RESERVAR(2) //--- if(m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] == -1) { m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] = ++m_pos; } //--- Char por char while(true) { //--- if(m_pos >= m_len) { return true; } //--- const uchar c = m_raw[m_pos]; //--- if(c == '\\') { m_pos += 2; continue; } if(c == '"') { //--- const int start = m_stak_pos[JSONPARSERBYLEO_SAX_STR_START]; m_in.OnString(start, m_pos - start); //--- m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] = -1; //--- m_pos++; //--- m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; break; } //--- m_pos++; } //--- // No enconto nada.. entonces.. sale noma vovemos luego //--- break; } case TSN_JSON_TOK_BOOL_TRUE: { //TSN_JSON_RESERVAR(1) // t r u e W // [-4] [-3] [-2] [-1] [0] //--- if(m_pos + 5 < m_len) { m_pos += 4; // Aumentamos y continuamos m_in.OnBool(true); m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; } break; } case TSN_JSON_TOK_BOOL_FALSE: { //TSN_JSON_RESERVAR(1) // f a l s e W // [] [-4] [-3] [-2 ] [-1] [0] //--- if(m_pos + 6 < m_len) { m_pos += 5; // Aumentamos y continuamos m_in.OnBool(false); m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; } break; } case TSN_JSON_TOK_NULL: { //TSN_JSON_RESERVAR(1) // n u l l W // [-4][-3][-2][-1][] //--- // Solo si hay 5 chars mas si no no hacemo snada if(m_pos + 5 < m_len) { m_pos += 4; // Aumentamos y continuamos m_in.OnNull(); m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; } break; } case TSN_JSON_TOK_COMMA: { //--- m_pos++; //--- if(m_stak_curr_state == TSN_JSON_CTX_IN_OBJ) m_next = TSN_JSON_TOK_SAX_FIND_KEY; else m_next = TSN_JSON_TOK_SAX_COM_KEY; break; } case TSN_JSON_TOK_LLAVE_INI: { //_-- m_in.OnStartObj(); //--- m_stak_curr++; m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_OBJ; m_stak_curr_state = TSN_JSON_CTX_IN_OBJ; //--- m_pos++; //--- m_next = TSN_JSON_TOK_SAX_LLAVE_INI; //--- break; } case TSN_JSON_TOK_COR_INI: { //_-- m_in.OnStartArr(); //--- m_stak_curr++; m_stak_state[m_stak_curr] = TSN_JSON_CTX_IN_ARR; m_stak_curr_state = TSN_JSON_CTX_IN_ARR; //--- m_pos++; //--- m_next = TSN_JSON_TOK_SAX_COR_INI; //--- break; } case TSN_JSON_TOK_LLAVE_END: { //--- m_stak_curr--; //--- m_in.OnEndObJ(); //--- if(m_stak_curr < 0) // termino { m_in.OnEndDocument(); JSONPARSERBYLEO_SAX_ON_FINISH return true; } //--- m_stak_curr_state = m_stak_state[m_stak_curr]; //--- m_pos++; //--- m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; //--- break; } case TSN_JSON_TOK_COR_END: { //--- m_stak_curr--; //--- m_in.OnEndArr(); //--- if(m_stak_curr < 0) // termino { m_in.OnEndDocument(); JSONPARSERBYLEO_SAX_ON_FINISH return true; } //--- m_stak_curr_state = m_stak_state[m_stak_curr]; //--- m_pos++; //--- m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; //--- break; } case TSN_JSON_TOK_KEY: { //--- if(m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] == -1) { m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] = ++m_pos; } //--- Char por char while(true) { //--- if(m_pos >= m_len) { return true; } //--- const uchar c = m_raw[m_pos]; //--- if(c == '\\') { m_pos += 2; continue; } if(c == '"') { //--- const int start = m_stak_pos[JSONPARSERBYLEO_SAX_STR_START]; m_in.OnKey(start, m_pos - start); //--- m_stak_pos[JSONPARSERBYLEO_SAX_STR_START] = -1; //--- m_pos++; //--- m_next = TSN_JSON_TOK_SAX_DOS_PUNTOS; break; } //--- m_pos++; } //--- break; } case TSN_JSON_TOK_INVALID: { m_last_err = TSN_JSON_ERR_INVALID_CHAR; JSONPARSERBYLEO_SAX_ON_FINISH return false; } case TSN_JSON_TOK_SAX_NO_NEXT_KEY: { while(true) { //--- if(m_pos >= m_len) return true; // esperando mas ... //--- const uchar _c = m_raw[m_pos]; if(_c < 33) { m_pos++; continue; } //--- m_next = g_table_json_tokens[_c]; if(((1 << m_next) & m_stak_curr_state) == 0) { JSONPARSERBYLEO_SAX_ON_FINISH m_last_err = TSN_JSON_ERR_MALFORDMED_JSON; return false; } break; } //--- break; } //--- case TSN_JSON_TOK_SAX_LLAVE_INI: { while(true) { //--- if(m_pos >= m_len) return true; // esperando mas ... //--- const uchar _c = m_raw[m_pos]; if(_c == '"') { m_next = TSN_JSON_TOK_KEY; break; } if(_c == '}') { m_next = TSN_JSON_TOK_LLAVE_END; break; } m_pos++; } //--- break; } //--- case TSN_JSON_TOK_SAX_COR_INI: { while(true) { //--- if(m_pos >= m_len) return true; // esperando mas ... //--- const uchar _c = m_raw[m_pos]; if(_c < 33) { m_pos++; continue; } m_next = g_table_json_tokens[_c]; if(((1 << m_next) & TSN_JSON_F_VAL_COR) == 0) { JSONPARSERBYLEO_SAX_ON_FINISH m_last_err = TSN_JSON_ERR_MALFORDMED_JSON; return false; } break; } break; } case TSN_JSON_TOK_SAX_FIND_KEY: { while(true) { //--- if(m_pos >= m_len) return true; // esperando mas ... //--- if(m_raw[m_pos] == '"') { m_next = TSN_JSON_TOK_KEY; break; } m_pos++; } break; } case TSN_JSON_TOK_SAX_COM_KEY: { while(true) { //--- if(m_pos >= m_len) return true; // esperando mas ... //--- const uchar _c = m_raw[m_pos]; if(_c < 33) { m_pos++; continue; } m_next = g_table_json_tokens[_c]; if(((1 << m_next) & TSN_JSON_F_ALL_I_VAL) == 0) { m_last_err = TSN_JSON_ERR_MALFORDMED_JSON; JSONPARSERBYLEO_SAX_ON_FINISH return false; } break; } break; } //--- case TSN_JSON_TOK_SAX_DBL: { // Comentarios para que no te pierdas // En si la tenica a usar es como ir lockeradno o teneinedo estaods con reuso de varialbes // m_prev_c continee el ultimo char.. lo uso priemro para poder evitar una lectura de array mas // y luego lo utilizo mas adalante para no vover a ver si m_pos e E\e // Dado que podria no entrar si eso pasa // Ahora otras vairalevbs presents son m_in_n esta es como mi avarialbe de estado // La uso para ver en que fase estoy // Luego tmabien se reusa m_last_number en el E\e para ver si tenia signo o no // Aqui si neceisto persistencia.. por eso reutilizo esa varialbe.. if(m_prev_c == 30) { if(m_in_n == 0) // etapa inicial 0 { m_in_n++; // 1 m_pos++; m_sax_frac_int = 0; m_sax_frac_digits = 0; } //--- while(true) { //--- if(m_pos >= m_len) return true; //--- const uchar c = m_raw[m_pos] ^ '0'; if(c > 9) { m_in_n = 0; // Vuelta en 0 (ahora para procesar lod giitos) m_prev_c = 0; // Ahora ya no podra ingresar a este if m_last_number_dbl += m_sax_frac_int * g_Exp10[m_sax_frac_digits]; break; } //--- m_sax_frac_int = (m_sax_frac_int << 3) + (m_sax_frac_int << 1) + c; m_sax_frac_digits++; m_pos++; } //--- // Pasamos con un char extra.. } //--- Ahora procesamos E if(m_prev_c == 15 || (m_raw[m_pos] | 0x20) == 'e') // e or E { //--- switch(m_in_n) { case 0: { m_in_n++; // 1 m_prev_c = 15; m_pos++; if(m_pos >= m_len) return true; // Salimos // Cae al siguiente } case 1: { m_in_n++; // 2 m_last_number = 1; // bool exp_sign = true; //--- const uchar _c = m_raw[m_pos]; if(_c == '-') { m_last_number = 0; // exp_sign = false; m_pos++; } else if(_c == '+') { m_pos++; } //--- if(m_pos >= m_len) return true; // Cae al siguiente } case 2: { m_in_n++; // 3 m_sax_frac_int = 0; // Cae al siguiente } case 3: { while(true) { const uchar _c = m_raw[m_pos] ^ '0'; if(_c < 9) { m_last_number_dbl *= m_last_number ? g_Pow10[m_sax_frac_int] : g_Exp10[m_sax_frac_int]; break; } //--- m_sax_frac_int = (m_sax_frac_int << 3) + (m_sax_frac_int << 1) + _c; //--- m_pos++; if(m_pos >= m_len) return true; } break; // Sale esta vez si.. } //--- default: { JSONPARSERBYLEO_SAX_ON_FINISH m_last_err = TSN_JSON_ERR_INVALID_STATE_IN_PARSE_DOUBLE_E; return false; } } } //--- Reinicio base m_in_n = 0; //--- TsnBitInterprete bit; bit.double_value = m_last_number_dbl; bit.long_value ^= (m_mask & (1LL << 63)); m_in.OnReal(bit.double_value); //--- m_last_number = 0; // reiniciamos (pude haber sido 1) m_mask = 0; // tambien a 0 //--- m_next = TSN_JSON_TOK_SAX_NO_NEXT_KEY; break; } case TSN_JSON_TOK_SAX_DOS_PUNTOS: { //--- Find : while(true) { //--- if(m_pos >= m_len) return true; // luego //--- if(m_raw[m_pos] == ':') { //--- m_pos++; // Luego del //--- Buscamos valor m_next = TSN_JSON_TOK_SAX_COM_KEY; break; } //--- m_pos++; } //--- break; } //--- default: { m_last_err = TSN_JSON_ERR_INVALID_CHAR; JSONPARSERBYLEO_SAX_ON_FINISH return false; } } } //--- return true; } } #endif // JSONPARSERBYLEO_SRC_JSONSAXIMP_MQH //+------------------------------------------------------------------+