//+------------------------------------------------------------------ß //| JAson | //| This software is licensed under the MIT https://goo.gl/eyJgHe | //+------------------------------------------------------------------+ #property copyright "Copyright © 2006-2017" #property version "1.12" #property strict //------------------------------------------------------------------ enum enJAType enum enJAType { jtUNDEF, jtNULL, jtBOOL, jtINT, jtDBL, jtSTR, jtARRAY, jtOBJ }; //------------------------------------------------------------------ class CJAVal class CJAVal { public: virtual void Clear (enJAType jt = jtUNDEF, bool savekey = false) { m_parent = NULL; if (!savekey) groupName = ""; m_type = jt; m_bv = false; m_iv = 0; m_dv = 0; m_prec = 5; m_sv = ""; ArrayResize (group, 0, 100); } virtual bool Copy (const CJAVal &a) { groupName = a.groupName; CopyData (a); return true; } virtual void CopyData (const CJAVal &a) { m_type = a.m_type; m_bv = a.m_bv; m_iv = a.m_iv; m_dv = a.m_dv; m_prec = a.m_prec; m_sv = a.m_sv; CopyArr (a); } virtual void CopyArr (const CJAVal &a) { int n = ArrayResize (group, ArraySize (a.group)); for (int i = 0; i < n; i++) { group [i] = a.group [i]; group [i].m_parent = GetPointer (this); } } public: CJAVal group []; // group string groupName; // group name string m_lkey; CJAVal *m_parent; enJAType m_type; bool m_bv; long m_iv; double m_dv; int m_prec; string m_sv; static int code_page; public: CJAVal () { Clear (); } CJAVal (CJAVal *aparent, enJAType atype) { Clear (); m_type = atype; m_parent = aparent; } CJAVal (enJAType t, string a) { Clear (); FromStr (t, a); } CJAVal (const int a) { Clear (); m_type = jtINT; m_iv = a; m_dv = (double)m_iv; m_sv = IntegerToString (m_iv); m_bv = m_iv != 0; } CJAVal (const long a) { Clear (); m_type = jtINT; m_iv = a; m_dv = (double)m_iv; m_sv = IntegerToString (m_iv); m_bv = m_iv != 0; } CJAVal (const double a, int aprec = -100) { Clear (); m_type = jtDBL; m_dv = a; if (aprec > -100) m_prec = aprec; m_iv = (long)m_dv; m_sv = DoubleToString (m_dv, m_prec); m_bv = m_iv != 0; } CJAVal (const bool a) { Clear (); m_type = jtBOOL; m_bv = a; m_iv = m_bv; m_dv = m_bv; m_sv = IntegerToString (m_iv); } CJAVal (const CJAVal &a) { Clear (); Copy (a); } ~CJAVal () { Clear (); } public: int Size () { return ArraySize (group); } virtual bool IsNumeric () { return m_type == jtDBL || m_type == jtINT; } virtual CJAVal *FindKey (string akey) { for (int i = Size () - 1; i >= 0; --i) if (group [i].groupName == akey) return GetPointer (group [i]); return NULL; } virtual CJAVal *HasKey (string akey, enJAType atype = jtUNDEF) { CJAVal *e = FindKey (akey); if (CheckPointer (e) != POINTER_INVALID) { if (atype == jtUNDEF || atype == e.m_type) return GetPointer (e); } return NULL; } virtual CJAVal *operator [] (string akey); virtual CJAVal *operator [] (int i); void operator= (const CJAVal &a) { Copy (a); } void operator= (const int a) { m_type = jtINT; m_iv = a; m_dv = (double)m_iv; m_bv = m_iv != 0; } void operator= (const long a) { m_type = jtINT; m_iv = a; m_dv = (double)m_iv; m_bv = m_iv != 0; } void operator= (const double a) { m_type = jtDBL; m_dv = a; m_iv = (long)m_dv; m_bv = m_iv != 0; } void operator= (const bool a) { m_type = jtBOOL; m_bv = a; m_iv = (long)m_bv; m_dv = (double)m_bv; } void operator= (string a) { m_type = (a != NULL) ? jtSTR : jtNULL; m_sv = a; m_iv = StringToInteger (m_sv); m_dv = StringToDouble (m_sv); m_bv = a != NULL; } bool operator== (const int a) { return m_iv == a; } bool operator== (const long a) { return m_iv == a; } bool operator== (const double a) { return m_dv == a; } bool operator== (const bool a) { return m_bv == a; } bool operator== (string a) { return m_sv == a; } bool operator!= (const int a) { return m_iv != a; } bool operator!= (const long a) { return m_iv != a; } bool operator!= (const double a) { return m_dv != a; } bool operator!= (const bool a) { return m_bv != a; } bool operator!= (string a) { return m_sv != a; } long ToInt () const { return m_iv; } double ToDbl () const { return m_dv; } bool ToBool () const { return m_bv; } string ToStr () { return m_sv; } virtual void FromStr (enJAType t, string a) { m_type = t; switch (m_type) { case jtBOOL: m_bv = (StringToInteger (a) != 0); m_iv = (long)m_bv; m_dv = (double)m_bv; m_sv = a; break; case jtINT: m_iv = StringToInteger (a); m_dv = (double)m_iv; m_sv = a; m_bv = m_iv != 0; break; case jtDBL: m_dv = StringToDouble (a); m_iv = (long)m_dv; m_sv = a; m_bv = m_iv != 0; break; case jtSTR: m_sv = Unescape (a); m_type = (m_sv != NULL) ? jtSTR : jtNULL; m_iv = StringToInteger (m_sv); m_dv = StringToDouble (m_sv); m_bv = m_sv != NULL; break; } } virtual string GetStr (char &js [], int i, int slen) { if (slen == 0) return ""; char cc []; ArrayCopy (cc, js, 0, i, slen); return CharArrayToString (cc, 0, WHOLE_ARRAY, CJAVal::code_page); } virtual void Set (const CJAVal &a) { if (m_type == jtUNDEF) m_type = jtOBJ; CopyData (a); } virtual void Set (const CJAVal &list []); virtual CJAVal *Add (const CJAVal &item) { if (m_type == jtUNDEF) m_type = jtARRAY; /*ASSERT(m_type==jtOBJ || m_type==jtARRAY);*/ return AddBase (item); } // добавление virtual CJAVal *Add (const int a) { CJAVal item (a); return Add (item); } virtual CJAVal *Add (const long a) { CJAVal item (a); return Add (item); } virtual CJAVal *Add (const double a, int aprec = -2) { CJAVal item (a, aprec); return Add (item); } virtual CJAVal *Add (const bool a) { CJAVal item (a); return Add (item); } virtual CJAVal *Add (string a) { CJAVal item (jtSTR, a); return Add (item); } virtual CJAVal *AddBase (const CJAVal &item) // добавление { int c = Size (); ArrayResize (group, c + 1, 100); group [c] = item; group [c].m_parent = GetPointer (this); return GetPointer (group [c]); } virtual CJAVal *New () { if (m_type == jtUNDEF) m_type = jtARRAY; /*ASSERT(m_type==jtOBJ || m_type==jtARRAY);*/ return NewBase (); } // добавление virtual CJAVal *NewBase () // добавление { int c = Size (); ArrayResize (group, c + 1, 100); return GetPointer (group [c]); } virtual string Escape (string a); virtual string Unescape (string a); public: virtual void Serialize (string &js, bool bf = false, bool bcoma = false); virtual string Serialize () { string js; Serialize (js); return js; } virtual bool Deserialize (char &js [], int slen, int &i); virtual bool ExtrStr (char &js [], int slen, int &i); virtual bool Deserialize (string js, int acp = CP_ACP) { int i = 0; Clear (); CJAVal::code_page = acp; char arr []; int slen = StringToCharArray (js, arr, 0, WHOLE_ARRAY, CJAVal::code_page); return Deserialize (arr, slen, i); } virtual bool Deserialize (char &js [], int acp = CP_ACP) { int i = 0; Clear (); CJAVal::code_page = acp; return Deserialize (js, ArraySize (js), i); } }; int CJAVal::code_page = CP_ACP; //------------------------------------------------------------------ operator[] CJAVal *CJAVal::operator[] (string akey) { if (m_type == jtUNDEF) m_type = jtOBJ; CJAVal *v = FindKey (akey); if (v) return v; CJAVal b (GetPointer (this), jtUNDEF); b.groupName = akey; v = Add (b); return v; } //------------------------------------------------------------------ operator[] CJAVal *CJAVal::operator[] (int i) { if (m_type == jtUNDEF) m_type = jtARRAY; while (i >= Size ()) { CJAVal b (GetPointer (this), jtUNDEF); if (CheckPointer (Add (b)) == POINTER_INVALID) return NULL; } return GetPointer (group [i]); } //------------------------------------------------------------------ Set void CJAVal::Set (const CJAVal &list []) { if (m_type == jtUNDEF) m_type = jtARRAY; int n = ArrayResize (group, ArraySize (list), 100); for (int i = 0; i < n; ++i) { group [i] = list [i]; group [i].m_parent = GetPointer (this); } } //------------------------------------------------------------------ Serialize void CJAVal::Serialize (string &js, bool bkey/*=false*/, bool coma/*=false*/) { if (m_type == jtUNDEF) return; if (coma) js += ","; if (bkey) js += StringFormat ("\"%s\":", groupName); int _n = Size (); switch (m_type) { case jtNULL: js += "null"; break; case jtBOOL: js += (m_bv ? "true" : "false"); break; case jtINT: js += IntegerToString (m_iv); break; case jtDBL: js += DoubleToString (m_dv, 5); break; case jtSTR: { string ss = Escape (m_sv); if (StringLen (ss) > 0) js += StringFormat ("\"%s\"", ss); else js += "null"; } break; case jtARRAY: js += "["; for (int i = 0; i < _n; i++) group [i].Serialize (js, false, i > 0); js += "]"; break; case jtOBJ: js += "{"; for (int i = 0; i < _n; i++) group [i].Serialize (js, true, i > 0); js += "}"; break; } } //------------------------------------------------------------------ Deserialize bool CJAVal::Deserialize (char &js [], int slen, int &i) { string num = "0123456789+-.eE"; int i0 = i; for (; i < slen; i++) { char c = js [i]; if (c == 0) break; switch (c) { case '\t': case '\r': case '\n': case ' ': // пропускаем из имени пробелы i0 = i + 1; break; case '[': // начало массива. создаём объекты и забираем из js { i0 = i + 1; if (m_type != jtUNDEF) // если значение уже имеет тип, то это ошибка { Print (groupName + " " + string (__LINE__)); return false; } m_type = jtARRAY; // задали тип значения i++; CJAVal val (GetPointer (this), jtUNDEF); while (val.Deserialize (js, slen, i)) { if (val.m_type != jtUNDEF) Add (val); if (val.m_type == jtINT || val.m_type == jtDBL || val.m_type == jtARRAY) i++; val.Clear (); val.m_parent = GetPointer (this); if (js [i] == ']') break; i++; if (i >= slen) { Print (groupName + " " + string (__LINE__)); return false; } } if (i >= slen) return false; return js [i] == ']' || js [i] == 0; } break; case ']': if (!m_parent) return false; return m_parent.m_type == jtARRAY; // конец массива, текущее значение должны быть массивом case ':': { if (m_lkey == "") { Print (groupName + " " + string (__LINE__)); return false; } CJAVal val (GetPointer (this), jtUNDEF); CJAVal *oc = Add (val); // тип объекта пока не определён oc.groupName = m_lkey; m_lkey = ""; // задали имя ключа i++; if (!oc.Deserialize (js, slen, i)) { Print (groupName + " " + string (__LINE__)); return false; } break; } case ',': // разделитель значений // тип значения уже должен быть определён i0 = i + 1; if (!m_parent && m_type != jtOBJ) { Print (groupName + " " + string (__LINE__)); return false; } else if (m_parent) { if (m_parent.m_type != jtARRAY && m_parent.m_type != jtOBJ) { Print (groupName + " " + string (__LINE__)); return false; } if (m_parent.m_type == jtARRAY && m_type == jtUNDEF) return true; } break; // примитивы могут быть ТОЛЬКО в массиве / либо самостоятельно case '{': // начало объекта. создаем объект и забираем его из js i0 = i + 1; if (m_type != jtUNDEF) // ошибка типа { Print (groupName + " " + string (__LINE__)); return false; } m_type = jtOBJ; // задали тип значения i++; if (!Deserialize (js, slen, i)) // вытягиваем его { Print (groupName + " " + string (__LINE__)); return false; } if (i >= slen) return false; return js [i] == '}' || js [i] == 0; break; case '}': return m_type == jtOBJ; // конец объекта, текущее значение должно быть объектом case 't': case 'T': // начало true case 'f': case 'F': // начало false if (m_type != jtUNDEF) // ошибка типа { Print (groupName + " " + string (__LINE__)); return false; } m_type = jtBOOL; // задали тип значения if (i + 3 < slen) { if (StringCompare (GetStr (js, i, 4), "true", false) == 0) { m_bv = true; i += 3; return true; } } if (i + 4 < slen) { if (StringCompare (GetStr (js, i, 5), "false", false) == 0) { m_bv = false; i += 4; return true; } } Print (groupName + " " + string (__LINE__)); return false; // не тот тип или конец строки break; case 'n': case 'N': // начало null if (m_type != jtUNDEF) // ошибка типа { Print (groupName + " " + string (__LINE__)); return false; } m_type = jtNULL; // задали тип значения if (i + 3 < slen) if (StringCompare (GetStr (js, i, 4), "null", false) == 0) { i += 3; return true; } Print (groupName + " " + string (__LINE__)); return false; // не NULL или конец строки break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': case '+': case '.': // начало числа { if (m_type != jtUNDEF) // ошибка типа { Print (groupName + " " + string (__LINE__)); return false; } bool dbl = false; // задали тип значения int is = i; while (js [i] != 0 && i < slen) { i++; if (StringFind (num, GetStr (js, i, 1)) < 0) break; if (!dbl) dbl = (js [i] == '.' || js [i] == 'e' || js [i] == 'E'); } m_sv = GetStr (js, is, i - is); if (dbl) { m_type = jtDBL; m_dv = StringToDouble (m_sv); m_iv = (long)m_dv; m_bv = m_iv != 0; } else // уточнии тип значения { m_type = jtINT; m_iv = StringToInteger (m_sv); m_dv = (double)m_iv; m_bv = m_iv != 0; } i--; return true; // отодвинулись на 1 символ назад и вышли break; } case '\"': // начало или конец строки if (m_type == jtOBJ) // если тип еще неопределён и ключ не задан { i++; int is = i; if (!ExtrStr (js, slen, i)) // это ключ, идём до конца строки { Print (groupName + " " + string (__LINE__)); return false; } m_lkey = GetStr (js, is, i - is); } else { if (m_type != jtUNDEF) // ошибка типа { Print (groupName + " " + string (__LINE__)); return false; } m_type = jtSTR; // задали тип значения i++; int is = i; if (!ExtrStr (js, slen, i)) { Print (groupName + " " + string (__LINE__)); return false; } FromStr (jtSTR, GetStr (js, is, i - is)); return true; } break; } } return true; } //------------------------------------------------------------------ ExtrStr bool CJAVal::ExtrStr (char &js [], int slen, int &i) { for (; js [i] != 0 && i < slen; i++) { char c = js [i]; if (c == '\"') break; // конец строки if (c == '\\' && i + 1 < slen) { i++; c = js [i]; switch (c) { case '/': case '\\': case '\"': case 'b': case 'f': case 'r': case 'n': case 't': break; // это разрешенные case 'u': // \uXXXX { i++; for (int j = 0; j < 4 && i < slen && js [i] != 0; j++, i++) { if (!((js [i] >= '0' && js [i] <= '9') || (js [i] >= 'A' && js [i] <= 'F') || (js [i] >= 'a' && js [i] <= 'f'))) // не hex { Print (groupName + " " + CharToString (js [i]) + " " + string (__LINE__)); return false; } } i--; break; } default: break; /*{ return false; } // неразрешенный символ с экранированием */ } } } return true; } //------------------------------------------------------------------ Escape string CJAVal::Escape (string a) { ushort as [], s []; int n = StringToShortArray (a, as); if (ArrayResize (s, 2 * n) != 2 * n) return NULL; int j = 0; for (int i = 0; i < n; i++) { switch (as [i]) { case '\\': s [j] = '\\'; j++; s [j] = '\\'; j++; break; case '"': s [j] = '\\'; j++; s [j] = '"'; j++; break; case '/': s [j] = '\\'; j++; s [j] = '/'; j++; break; case 8: s [j] = '\\'; j++; s [j] = 'b'; j++; break; case 12: s [j] = '\\'; j++; s [j] = 'f'; j++; break; case '\n': s [j] = '\\'; j++; s [j] = 'n'; j++; break; case '\r': s [j] = '\\'; j++; s [j] = 'r'; j++; break; case '\t': s [j] = '\\'; j++; s [j] = 't'; j++; break; default: s [j] = as [i]; j++; break; } } a = ShortArrayToString (s, 0, j); return a; } //------------------------------------------------------------------ Unescape string CJAVal::Unescape (string a) { ushort as [], s []; int n = StringToShortArray (a, as); if (ArrayResize (s, n) != n) return NULL; int j = 0, i = 0; while (i < n) { ushort c = as [i]; if (c == '\\' && i < n - 1) { switch (as [i + 1]) { case '\\': c = '\\'; i++; break; case '"': c = '"'; i++; break; case '/': c = '/'; i++; break; case 'b': c = 8; /*08='\b'*/; i++; break; case 'f': c = 12; /*0c=\f*/ i++; break; case 'n': c = '\n'; i++; break; case 'r': c = '\r'; i++; break; case 't': c = '\t'; i++; break; case 'u': // \uXXXX { i += 2; ushort k = 0; for (int jj = 0; jj < 4 && i < n; jj++, i++) { c = as [i]; ushort h = 0; if (c >= '0' && c <= '9') h = c - '0'; else if (c >= 'A' && c <= 'F') h = c - 'A' + 10; else if (c >= 'a' && c <= 'f') h = c - 'a' + 10; else break; // не hex k += h * (ushort)pow (16, (3 - jj)); } i--; c = k; break; } } } s [j] = c; j++; i++; } a = ShortArrayToString (s, 0, j); return a; } //+------------------------------------------------------------------+