Extindeți TMS WEB Core cu bibliotecile JS cu Andrew: Epic JSON Primer (partea 2)
20: Performanță relativă.
Am auzit adesea că, deși este posibil să folosim varianta TJSONObject a acestor abordări (codul PAS la care am lucrat atât de sârguincios), este de preferat să folosim varianta WC, deoarece va fi mai performantă. Vom pune acest lucru la încercare aici într-un exemplu artificial. Adesea, lizibilitatea și reutilizarea codului sunt mai importante decât performanța directă, în special pentru codul dificil care nu este executat frecvent. Dar, în același timp, cu siguranță apar situații în care un pic de cod este executat frecvent într-o buclă strânsă și este important să stoarceți fiecare fragment de performanță.
procedura TForm1.WebButton1Click(Expeditor: TObject); var WC_Object: TJSObject; PAS_Object: TJSONObject; ElapsedTime: TDateTime; i: întreg; Număr: întreg; ÎNCEPE ElapsedTime := Acum; // JS creează 1.000.000 de obiecte asm var JS_Object = {}; pentru (var i = 0; i <= 999999; i++) { JS_Object['test '+i] = 'test '+i; } Sfârşit; console.log('JS Create: '+IntToStr(MillisecondsBetween(Now,ElapsedTime))+' ms'); ElapsedTime := Acum; // JS Count Objects asm Count = Object.keys(JS_Object).lungime; Sfârşit; console.log('Număr JS: '+IntToStr(MillisecondsBetween(Acum,ElapsedTime))+' ms'); ElapsedTime := Acum; // WC Creați 1.000.000 de obiecte WC_Object := TJSObject.new; pentru i := 0 la 999999 do WC_Object['test '+IntToStr(i)] := 'test '+IntToStr(i); console.log('WC Create: '+IntToStr(MillisecondsBetween(Now,ElapsedTime))+' ms'); ElapsedTime := Acum; // WC Count Objects Număr := lungime(TJSObject.keys(WC_Object)); console.log('Număr WC: '+IntToStr(MillisecondsBetween(Acum,ElapsedTime))+' ms'); ElapsedTime := Acum; // PAS creează 10.000 de obiecte PAS_Object := TJSONObject.Create; pentru i := 0 până la 9999 do PAS_Object.AddPair('test '+IntToStr(i),'test '+IntToStr(i)); console.log('PAS Create: '+IntToStr(MillisecondsBetween(Now,ElapsedTime))+' ms'); ElapsedTime := Acum; // PAS Count Objects Count := PAS_Object.Count; console.log('Număr PAS: '+IntToStr(MillisecondsBetween(Acum,ElapsedTime))+' ms'); Sfârşit; Ieșire console.log: JS Create: 1154 ms Număr JS: 301 ms Creare WC: 1006 ms Număr WC: 272 ms PAS Creare: 27439 ms Număr PAS: 0 ms
Așa că a fost deschis ochii! De fapt, a trebuit să reduc bucla PAS la doar 10.000 de obiecte în loc de cele 1.000.000 de obiecte ale buclelor JS și WC doar pentru a se finaliza. Și totuși a fost nevoie de aproape 30 de ori mai mult pentru a crea obiectele JSON (deci extrapolând, este de 3.000 de ori mai lent?!) Se pare că cineva undeva are ceva de făcut de optimizare. Rețineți că numărul este banal în cazul PAS, așa că nici măcar nu se înregistrează, în cazul în care, ca și în cazul JS și WC, trebuie de fapt să depună destulă muncă pentru a ajunge la acel număr. La pachet? Ei bine, la fel ca înainte, într-adevăr, doar surprinzător că penalizarea este atât de mare. Sperăm că situația se va îmbunătăți în timp. Sau dacă nu, acesta este un argument destul de solid pentru migrarea codului PAS care se află în orice fel de buclă pentru a utiliza varianta JS sau WC. Pentru a fi corect, acesta este probabil singurul cod din acest articol în care orice diferență de performanță ar fi chiar detectabilă.
De asemenea, rețineți că, rulând testul din nou și din nou, timpii JS și WC Create variază destul de mult, uneori până la jumătate. Dar Count times nu au fluctuat prea mult deloc. PAS a rămas în mare parte același. Acest lucru s-ar putea datora multor lucruri, dar există o mulțime de optimizare JS care se întâmplă peste tot, așa că nu este prea surprinzător. Totuși, este o provocare să veniți cu criterii de referință rezonabile - trebuie să rulați o mulțime de teste pentru a obține o cifră medie pentru ca aceasta să fie semnificativă și chiar și atunci trebuie să luați în considerare condițiile din jurul testului cu mare atenție - a fost aplicația s-a încărcat deja, a existat o animație care rulează undeva etc.
21: Iterați prin elementele obiect JSON.
Am avut deja exemple de astfel de lucruri ici și colo, dar iată un exemplu mai explicit pentru toate cele trei variante.
procedura TForm1.WebButton1Click(Expeditor: TObject); var WC_Object: TJSObject; PAS_Object: TJSONObject; i: întreg; const SampleObjectData = '{"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"}'; ÎNCEPE asm var JS_Object = JSON.parse(SampleObjectData); Sfârşit; WC_Object := TJSJSON.parseObject(SampleObjectData); PAS_Object := TJSONObject.ParseJSONValue(SampleObjectData) ca TJSONObject; asm pentru (var Key in JS_Object) { console.log(„Cheie JS: „+Key+” Valoare: „+JS_Object[Key]) } Sfârşit; i := 0; în timp ce (i < lungime(TJSOBject.keys(WC_Object))) do ÎNCEPE console.log('Cheie WC: '+șir(TJSObject.keys(WC_Object)[i])+' Valoare: '+șir(WC_Object[TJSObject.keys(WC_Object)[i]])); i := i + 1; Sfârşit; i := 0; în timp ce (i < PAS_Object.Count) do ÎNCEPE console.log('Cheie PAS: '+PAS_Object.Pairs[i].JSONString.Value+' Valoare: '+PAS_Object.Pairs[i].JSONValue.Value); i := i + 1; Sfârşit; Sfârşit; Ieșire console.log: Cheie JS: măr Valoare: fructe Cheie JS: banană Valoare: fructe Cheie JS: portocaliu Valoare: fructe Cheie JS: morcov Valoare: legume Cheie JS: cartof Valoare: legume Cheie WC: măr Valoare: fructe Cheie WC: banană Valoare: fructe Cheie WC: portocaliu Valoare: fructe Cheie WC: morcov Valoare: legume Cheie WC: cartof Valoare: legume Cheie PAS: măr Valoare: fructe Cheie PAS: banană Valoare: fructe Cheie PAS: portocaliu Valoare: fructe Cheie PAS: morcov Valoare: legume Cheie PAS: cartof Valoare: legume
22: traversează întregul obiect JSON.
Acum avem tot ce ne trebuie pentru a aborda orice tip de JSON care ne iese în cale. De obicei, nu este nevoie să traversați direct un întreg obiect JSON. În mod normal, căutați doar să alegeți anumite valori sau să actualizați altele. Totuși, un caz de utilizare este acela de a scoate o versiune formatată a obiectului JSON. Există o mulțime de resurse online pentru a face acest tip de lucru pentru dvs. - doar copiați și lipiți JSON-ul pe care doriți să îl formatați și va scoate o versiune mai frumoasă (drăguță sau înfrumusețată) destul de ușor. Aici facem ceva similar, adnotând orice găsim. Rețineți că rezultatul nu este deloc JSON, ci doar o listă cu ceea ce este în ea și detalii despre tipurile de date. Cu toate acestea, ar trebui să poată regurgita orice JSON prin care treceți. Nu este frumos, dar ar trebui să acopere totul.
procedura TForm1.WebButton1Click(Expeditor: TObject); var WC_Object: TJSObject; PAS_Object: TJSONObject; const SampleObjectData = '[[[123,{"key":true,"other key":"abc","mai multe chei":[1,2,false]},null]]]'; funcția WC_Print(Element:TJSObject; Indentation:String; Key:String):String; var str: String; tip: String; ÎNCEPE str := ''; tip:= ''; if (Element = nil) then begin str := 'Null (Null)'; tip := 'Null'; Sfârşit else if (Element.ToString = 'true') then begin str := 'True (Boolean)'; tip := 'Boolean'; Sfârşit else if (Element.ToString = 'false') then begin str := 'False (Boolean)'; tip := 'Boolean'; Sfârşit else if (TJSJSON.stringify(Element).startsWith('"')) atunci începe str := Element.toString+' (String)'; typ := 'String'; sfârşit else if (TJSJSON.stringify(Element).startsWith('{')) then begin str := 'Obiect {'; typ := 'Obiect'; Sfârşit else if (TJSJSON.stringify(Element).startsWith('[')) atunci începe str := 'Matrice ['; typ := 'Obiect'; Sfârşit else begin str := Element.ToString+' (Număr)'; tip := 'Număr'; Sfârşit; dacă (Cheie = '') apoi console.log(Indentation+' '+str) else console.log(Indentation+' '+Key+': '+str); Rezultat := tip; Sfârşit; procedura WC_Output(JSONObject:TJSObject; Indentation:String); var i: întreg; tip: sfoară; const indentare = '____'; ÎNCEPE dacă (TJSJSON.stringify(JSONObject).startsWith('[')) atunci ÎNCEPE dacă (Indentație = '') atunci ÎNCEPE console.log('WC ['); Indentare := indentare; Sfârşit; pentru i := 0 la (lungime(TJSObject.keys(JSONObject))-1) nu ÎNCEPE tip := WC_Print(TJSObject(JSONObject[String(TJSObject.keys(JSONObject)[i])]), Indentation, IntToStr(i)); if (typ = 'Obiect') atunci ÎNCEPE WC_Output(TJSObject(JSONObject[String(TJSObject.keys(JSONObject)[i])]), Indentation+indent); console.log(Indentation+' }'); Sfârşit else if (typ = 'Matrice') atunci ÎNCEPE WC_Output(TJSObject(JSONObject[String(TJSObject.keys(JSONObject)[i])]), Indentation+indent); console.log(Indentation+' ]'); Sfârşit; Sfârşit; if (Indentation = indent) atunci console.log(' ]'); Sfârşit else if (TJSJSON.stringify(JSONObject).startsWith('{')) atunci ÎNCEPE dacă (Indentație = '') atunci ÎNCEPE console.log('WC {'); Indentare := '____'; Sfârşit; pentru i := 0 la (lungime(TJSObject.keys(JSONObject))-1) nu ÎNCEPE tip := WC_Print(TJSObject(JSONObject[String(TJSObject.keys(JSONObject)[i])]), Indentation, string(TJSObject.keys(JSONObject)[i])); if (typ = 'Obiect') atunci ÎNCEPE WC_Output(TJSObject(JSONObject[String(TJSObject.keys(JSONObject)[i])]), Indentation+indent); console.log(Indentation+' }'); Sfârşit else if (typ = 'Matrice') atunci ÎNCEPE WC_Output(TJSObject(JSONObject[String(TJSObject.keys(JSONObject)[i])]), Indentation+indent); console.log(Indentation+' ]'); Sfârşit; Sfârşit; if (Indentation = indent) atunci console.log('}'); Sfârşit altfel ÎNCEPE WC_Print(JSONObject, Indentation,'') Sfârşit; Sfârşit; funcția PAS_Print(Element:TJSONValue; Indentation:String; Key:String):String; var str: String; tip: String; ÎNCEPE str := ''; tip:= ''; if (Element.ClassName = 'TJSONNull') then begin str := 'Null (Null)'; tip := 'Null'; Sfârşit else if (Element.ClassName = 'TJSONTrue') then begin str := 'True (Boolean)'; tip := 'Boolean'; Sfârşit else if (Element.ClassName = 'TJSONFalse') then begin str := 'False (Boolean)'; tip := 'Boolean'; Sfârşit else if (Element.ClassName = 'TJSONString') atunci începe str := Element.toString+' (String)'; typ := 'Șir'; Sfârşit else if (Element.ClassName = 'TJSONNumber') atunci începe str := Element.toString+' (Număr)'; typ := 'Șir'; Sfârşit else if (Element.ClassName = 'TJSONObject') then begin str := 'Obiect {'; typ := 'Obiect'; Sfârşit else if (Element.ClassName = 'TJSONArray') atunci începe str := 'Matrice ['; typ := 'Obiect'; Sfârşit; dacă (Cheie = '') apoi console.log(Indentation+' '+str) else console.log(Indentation+' '+Key+': '+str); Rezultat := tip; Sfârşit; procedura PAS_Output(JSONObject:TJSONObject; Indentation:String); var i: întreg; tip: sfoară; const indentare = '____'; ÎNCEPE dacă (JSONObject.ClassName = 'TJSONArray'), atunci ÎNCEPE dacă (Indentație = '') atunci ÎNCEPE console.log('PAS ['); Indentare := indentare; Sfârşit; for i := 0 to ((JSONObject as TJSONArray).Count - 1) do ÎNCEPE typ := PAS_Print((JSONObject ca TJSONArray)[i] ca TJSONValue, Indentation, IntToStr(i)); if (typ = 'Obiect') atunci ÎNCEPE PAS_Output((JSONObject ca TJSONArray)[i] ca TJSONObject, Indentation+indent); console.log(Indentation+' }'); Sfârşit else if (typ = 'Matrice') atunci ÎNCEPE PAS_Output((JSONObject ca TJSONArray)[i] ca TJSONArray, Indentation+indent); console.log(Indentation+' ]'); Sfârşit; Sfârşit; if (Indentation = indent) atunci console.log(']'); Sfârşit else if (JSONObject.ClassName = 'TJSONObject'), atunci ÎNCEPE dacă (Indentație = '') atunci ÎNCEPE console.log('PAS {'); Indentare := '____'; Sfârşit; pentru i := 0 la (JSONObject.Count - 1) do ÎNCEPE typ := PAS_Print(JSONObject.Items[i], Indentation, JSONObject.Pairs[i].JSONString.Value); if (typ = 'Obiect') atunci ÎNCEPE PAS_Output(JSONObject.Items[i] ca TJSONObject, Indentation+indent); console.log(Indentation+' }'); Sfârşit else if (typ = 'Matrice') atunci ÎNCEPE PAS_Output(JSONObject.Items[i] ca TJSONArray, Indentation+indent); console.log(Indentation+' ]'); Sfârşit; Sfârşit; if (Indentation = indent) atunci console.log('}'); Sfârşit altfel ÎNCEPE PAS_Print(JSONObject, Indentation,'') Sfârşit; Sfârşit; ÎNCEPE asm var JS_Object = JSON.parse(SampleObjectData); Sfârşit; WC_Object := TJSJSON.parseObject(SampleObjectData); PAS_Object := TJSONObject.ParseJSONValue(SampleObjectData) ca TJSONObject; asm funcția JS_Print(Element, Indentare, Cheie) { var str = ''; var typ = ''; if (Element === null) { str = 'Null (Null)'; tip = 'Null'; } else if (Element === true) { str = 'True (Boolean)'; typ = 'Boolean'; } else if (Element === false) { str = 'Fals (Boolean)'; typ = 'Boolean'; } else if (Array.isArray(Element)) { str = 'Matrice ['; tip = 'Matrice'; } else if (typeof Element === 'șir') { str = '"'+Element+'" (Șir)'; typ = 'Șir'; } else if (typeof Element === 'număr') { str = Element+' (Număr)'; tip = „Număr”; } else if (typeof Element === 'obiect') { str = 'Obiect {'; typ = 'Obiect'; } if (Cheie === nedefinit) { console.log(Indentation+' '+str); } else { console.log(Indentation+' '+Key+': '+str); } returnare (tip) } funcția JS_Output(JSONObject, Indentation) { const indent = '____'; dacă (Array.isArray(JSONObject)) { if (Indentație == '') { console.log("JS ["); Indentation = indentare; } pentru (var i = 0; i < JSONObject.length; i++) { comutator (JS_Print(JSONObject[i], Indentation,i)) { caz „Obiect”: JS_Output(JSONObject[i], Indentation+indent); console.log(Indentation+" }"); pauză; caz „Matrice”: JS_Output(JSONObject[i],Indentation+indent); console.log(Indentation+" ]"); pauză; } } if (Indentation == indent) { console.log("]") } } else if (tip de JSONObject === „obiect”) { if (Indentație == '') { console.log("JS {"); Indentație = „____”; } pentru (cheie var în JSONObject) { comutator (JS_Print(JSONObject[cheie],Indentation,key)) { caz „Obiect”: JS_Output(JSONObject[cheie],Indentation+indent); console.log(Indentation+" }"); pauză; caz „Matrice”: JS_Output(JSONObject[cheie],Indentation+indent); console.log(Indentation+" ]"); pauză; } } if (Indentation == indent) { console.log("}") } } else { JS_Print(JSONObject,Indentation); } } Sfârşit; asm JS_Output(JS_Object,''); Sfârşit; WC_Output(WC_Object,''); PAS_Output(PAS_Object,''); Sfârşit; Ieșirea console.log (doar JS, WC și PAS sunt aceleași): JS [ ____ 0: Matrice [ ________ 0: Matrice [ ____________ 0: 123 (număr) ____________ 1: Obiect { cheie ________________: adevărat (boolean) ________________ altă cheie: „abc” (Șir) ________________ mai multe chei: Array [ ____________________ 0: 1 (număr) ____________________ 1: 2 (număr) ____________________ 2: Fals (boolean) ________________ ] ____________ } ____________ 2: nul (nul) ________ ] ____ ] ]
23: Comparați JSON.
Cel mai rapid (sau cel puțin, cel mai simplu) mod de a compara două obiecte JSON ar fi să le convertiți în șiruri de caractere și să verificați dacă se potrivesc. Dar acest lucru poate să nu fie corect, având în vedere natura neordonată a elementelor obiect JSON. Dacă tot ce ai este o matrice sau dacă știi că obiectul tău este sortat după cheie sau printr-o altă metodă deterministă (ai creat elementele într-o anumită ordine, de exemplu), atunci compararea șirurilor ar putea funcționa în continuare. Dar dacă nu este cazul, este necesară o altă abordare pentru a determina această echivalență „semantică”. Aici repetăm unul dintre obiectele JSON și apoi verificăm că putem găsi totul în locul potrivit într-un al doilea obiect JSON. Pentru obiecte, doar căutăm cheile, astfel încât acestea să se potrivească și ordinea nu contează. Pentru Arrays, folosim indexul și ordinea contează într-adevăr. Această abordare face, de asemenea, posibilă ajustarea strictității comparației. De exemplu, dacă ați dori să aveți o potrivire care nu ține seama de majuscule și minuscule pe valorile șirului, de exemplu, sau mai puțină acuratețe (sau mai mult!) atunci când vine vorba de compararea numerelor.
procedura TForm1.WebButton1Click(Expeditor: TObject);
var
WC_Object1: TJSObject;
WC_Object2: TJSObject;
PAS_Object1: TJSONObject;
PAS_Object2: TJSONObject;
const
SampleObjectData1 = '[[[123,{"key":true,"other key":"abc","mai multe chei":[1,3,false]},null,[{"a":"1", "b":2,"c":"3"}]]]]';
SampleObjectData2 = '[[[123,{"other key":"abc","key":true,"mai multe chei":[1,3,false]},null,[{"c":"3", "b":2,"a":"1"}]]]]';
funcția WC_Compare(Element1: TJSObject; Element2:TJSObJect):Boolean;
var
test: boolean;
i: întreg;
ÎNCEPE
test := adevărat;
dacă ((Element1 = zero) și (Element2 = zero)) atunci testează := adevărat
else if (Element1 = nil) sau (Element2 = nil) atunci testează := fals
else if ((Element1.toString = 'adevărat') şi (Element2.toString = 'adevărat')) atunci testează := adevărat
else if ((Element1.toString = 'false') și (Element2.toString = 'false')) atunci testează := adevărat
altfel dacă (TJSJSON.stringify(Element1).startsWith('[')) și (TJSJSON.stringify(Element2).startsWith('[')) și (lungime(TJSObject.keys(Element1)) = lungime(TJSObject.keys (Element2))) atunci
ÎNCEPE
pentru i := 0 la (lungime(TJSObject.keys(Element1))-1) nu
ÎNCEPE
dacă nu (WC_Compare(TJSObject(Element1[String(TJSObject.keys(Element1)[i])]),TJSObject(Element2[String(TJSObject.keys(ELement2)[i])]))) atunci
ÎNCEPE
test := fals;
Sfârşit;
Sfârşit;
Sfârşit
altfel dacă (TJSJSON.stringify(Element1).startsWith('{')) și (TJSJSON.stringify(Element2).startsWith('{')) și (lungime(TJSObject.keys(Element1)) = lungime(TJSObject.keys (Element2))) atunci
ÎNCEPE
pentru i := 0 la (lungime(TJSObject.keys(Element1))-1) nu
ÎNCEPE
dacă nu (WC_Compare(TJSObject(Element1[String(TJSObject.keys(Element1)[i])]),TJSObject(Element2[String(TJSObject.keys(Element1)[i])]))) atunci
ÎNCEPE
test := fals;
Sfârşit;
Sfârşit;
Sfârşit
altfel dacă (TJSJSON.stringify(Element1).startsWith('"')) și (TJSJSON.stringify(Element2).startsWith('"')) și (Element1.toString = Element2.toString) atunci testează := adevărat
else if (Element1.toString = Element2.toString) atunci testează := adevărat
altfel
ÎNCEPE
test := fals;
Sfârşit;
// dacă nu(test), atunci console.log(TJSJSON.stringify(Element1)+' <> '+TJSJSON.stringify(Element2));
Rezultat := test;
Sfârşit;
funcția PAS_Compare(Element1: TJSONValue; Element2:TJSONValue):Boolean;
var
test: boolean;
i: întreg;
ÎNCEPE
test := adevărat;
dacă ((Element1.ClassName = 'TJSONNull') și (Element2.Classname = 'TJSONNull')) atunci testează := adevărat
else if ((Element1.ClassName = 'TJSONNull') sau (Element2.Classname = 'TJSONNull')) atunci testează := false
else if ((Element1.ClassName = 'TJSONTrue') și (Element2.Classname = 'TJSONTrue')) atunci testează := adevărat
else if ((Element1.ClassName = 'TJSONFalse') și (Element2.Classname = 'TJSONFalse')) atunci testează := adevărat
else if ((Element1.ClassName = 'TJSONString') și (Element2.Classname = 'TJSONString')) și (Element1.ToString = Element2.ToString) atunci testează := adevărat
else if ((Element1.ClassName = 'TJSONNumber') și (Element2.Classname = 'TJSONNumber')) și (Element1.ToString = Element2.ToString) atunci testează := adevărat
else if ((Element1.ClassName = 'TJSONArray') și (Element2.Classname = 'TJSONArray')) și ((Element1 ca TJSONArray).Count = (Element2 ca TJSONArray).Count) atunci
ÎNCEPE
for i := 0 to ((Element1 as TJSONArray).Count - 1) do
ÎNCEPE
dacă nu(PAS_Compare((Element1 ca TJSONArray).Items[i], ((Element2 ca TJSONArray).Items[i]))) atunci
ÎNCEPE
test := fals;
Sfârşit;
Sfârşit;
Sfârşit
else if ((Element1.ClassName = 'TJSONObject') și (Element2.Classname = 'TJSONObject')) și ((Element1 ca TJSONOBject).Count = (Element2 ca TJSONObject).Count) atunci
ÎNCEPE
for i := 0 to ((Element1 as TJSONObject).Count - 1) do
ÎNCEPE
dacă nu(PAS_Compare((Element1 ca TJSONObject).Pairs[i].JSONValue,(Element2 ca TJSONObject).Get((Element1 ca TJSONObject).Pairs[i].JSONString.value).JSONValue)) atunci
ÎNCEPE
test := fals;
Sfârşit;
Sfârşit;
Sfârşit
altfel
ÎNCEPE
dacă Element1.ToString <> Element2.toString
apoi testeaza := false
test else := adevărat;
Sfârşit;
// dacă nu (test), atunci console.log(Element1.ToString+' <> '+Element2.ToString);
Rezultat := test;
Sfârşit;
ÎNCEPE
asm var JS_Object1 = JSON.parse(SampleObjectData1); Sfârşit;
asm var JS_Object2 = JSON.parse(SampleObjectData2); Sfârşit;
WC_Object1 := TJSJSON.parseObject(SampleObjectData1);
WC_Object2 := TJSJSON.parseObject(SampleObjectData2);
PAS_Object1 := TJSONObject.ParseJSONValue(SampleObjectData1) ca TJSONObject;
PAS_Object2 := TJSONObject.ParseJSONValue(SampleObjectData2) ca TJSONObject;
asm
funcția JS_Compare(Element1, Element2) {
var test = adevărat;
dacă ((Element1 === nul) && (Element2 === nul)) {test = adevărat}
else if ((Element1 === nul) || (Element2 === nul)) {test = fals}
else if ((Element1 === adevărat) && (Element2 === adevărat)) {test = adevărat}
else if ((Element1 === fals) && (Element2 === fals)) {test = adevărat}
else if ((Array.isArray(Element1)) && (Array.isArray(Element2)) && (Element1.length == Element2.length)) {
pentru (var i = 0; i < Element1.lungime; i++) {
dacă (!JS_Compare(Element1[i],Element2[i])) {
test = fals;
}
}
}
else if ((tip de Element1 === „șir”) && (tip de Element2 === „șir”) && (Element1 === Element2)) {test = adevărat}
else if ((tip de Element1 === „număr”) && (tip de Element2 === „număr”) && (Element1 === Element2)) {test = adevărat}
else if ((tip de Element1 === „obiect”) && (tip de Element2 === „obiect”) && (Object.keys(Element1).length == Object.keys(Element2).lungime)) {
pentru (cheie var în Element1) {
dacă (!JS_Compare(Element1[cheie],Element2[cheie])) {
test = fals;
}
}
}
else {
test = fals;
}
// dacă (!test) {console.log(JSON.stringify(Element1)+' <> '+JSON.stringify(Element2))};
return(test);
}
Sfârşit;
asm console.log('JS '+JS_Compare(JS_Object1, JS_Object2)); Sfârşit;
console.log('WC '+BoolToStr(WC_Compare(WC_Object1,WC_Object2),true));
console.log('PAS '+BoolToStr(PAS_Compare(PAS_Object1,PAS_Object2),true));
Sfârşit;
Ieșire console.log:
JS adevărat
WC Adevărat
PAS Adevărat
24: Sortați elementele obiectului JSON.
Obiectele JSON nu sunt ordonate, dar adesea este de dorit să le afișați sortate. Rețineți că nu actualizăm în mod deliberat obiectul JSON cu elementele sortate, ci mai degrabă repetăm prin ele, astfel încât rezultatul să fie sortat. Esența acestui lucru este doar extragerea tuturor cheilor din obiectul JSON, sortarea cheilor după orice metodă este la îndemână și apoi afișarea cheilor în ordinea lor nou sortată împreună cu valoarea corespunzătoare fiecăreia.
procedura TForm1.WebButton1Click(Expeditor: TObject); var WC_Object: TJSObject; PAS_Object: TJSONObject; i: întreg; WC_Keys: TStringList; PAS_Keys: TStringList; const SampleObjectData = '{"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"}'; ÎNCEPE asm var JS_Object = JSON.parse(SampleObjectData); Sfârşit; WC_Object := TJSJSON.parseObject(SampleObjectData); PAS_Object := TJSONObject.ParseJSONValue(SampleObjectData) ca TJSONObject; asm var chei = Object.keys(JS_Object).sort(); pentru (var i = 0; i < keys.length; i++) { console.log('Cheie JS: '+keys[i]+' Value: '+JS_Object[keys[i]]) } Sfârşit; // Există și alte moduri de sortare, dar nu am avut prea mult noroc aici. // De exemplu, System.Generics.Collections ar bloca compilatorul din anumite motive WC_Keys := TStringList.Create; pentru i := 0 la lungime(TJSObject.keys(WC_Object))-1 do WC_Keys.Add(TJSObject.keys(WC_Object)[i]); WC_Keys.sort; pentru i := 0 la WC_Keys.Count -1 do console.log('Cheie WC: '+WC_Keys[i]+' Valoare: '+string(WC_Object[WC_Keys[i]])); // Aceeași abordare folosită aici din același motiv PAS_Keys := TStringList.Create; pentru i := 0 la PAS_Object.Count-1 do PAS_Keys.Add(PAS_Object.Pairs[i].JSONString.Value); PAS_Keys.sort; pentru i := 0 la PAS_Keys.Count -1 do console.log('Cheie PAS: '+PAS_Keys[i]+' Valoare: '+PAS_Object.Get(PAS_Keys[i]).JSONValue.ToString); Sfârşit; Ieșire console.log: Cheie JS: măr Valoare: fructe Cheie JS: banană Valoare: fructe Cheie JS: morcov Valoare: legume Cheie JS: portocaliu Valoare: fructe Cheie JS: cartof Valoare: legume Cheie WC: măr Valoare: fructe Cheie WC: banană Valoare: fructe Cheie WC: morcov Valoare: legume Cheie WC: portocaliu Valoare: fructe Cheie WC: cartof Valoare: legume Cheie PAS: măr Valoare: „fructe” Cheie PAS: banană Valoare: „fructe” Cheie PAS: morcov Valoare: „legume” Cheie PAS: portocaliu Valoare: „fructe” Cheie PAS: cartof Valoare: "legume"
25: Mutare de la JS la WC sau PAS.
Să presupunem că ați folosit JavaScript pentru a crea un pic de JSON sau se întâmplă să îl aveți dintr-un cod JavaScript din alt motiv, dar apoi doriți să-l faceți referire în Delphi folosind fie variantele WC, fie PAS pe care le-am folosit până acum. Aici definim un JS_Object ca o variabilă Delphi de tip JSValue, astfel încât să putem face referire la el mai târziu. Și apoi îl accesăm folosind doar funcțiile WC și funcțiile PAS pe care le-am folosit până acum, fără a fi nevoie măcar să definim variabile pentru a le păstra.
procedura TForm1.WebButton1Click(Expeditor: TObject); var JS_Object: JSValue; const SampleObjectData = '{"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"}'; ÎNCEPE asm JS_Object = JSON.parse(SampleObjectData); Sfârşit; // WC: cod Delphi pentru a accesa obiectul JSON JavaScript // Încheierea lui în TJSObject() îl face echivalent cu un WC TJSObject console.log('WC: '+TJSJSON.stringify(JS_Object)); console.log('WC: '+IntToStr(lungime(TJSObject.keys(TJSObject(JS_Object))))); // PAS: Conversia la un TJSONObject prin Strings funcționează console.log('PAS: '+(TJSONObject.parseJSONValue(TJSJSON.stringify(JS_Object)) ca TJSONObject).ToString); console.log('PAS: '+IntToStr((TJSONObject.parseJSONValue(TJSJSON.stringify(JS_Object)) ca TJSONObject).Count)); Sfârşit; Ieșire console.log: WC: {"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"} WC: 5 PAS: {"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"} PAS: 5
26: Mutați de la WC la JS sau PAS.
Aici, WC_Object poate fi pur și simplu accesat direct în JS. Și codul PAS este practic același ca data trecută.
procedura TForm1.WebButton1Click(Expeditor: TObject); var WC_Object: TJSObject; const SampleObjectData = '{"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"}'; ÎNCEPE WC_Object := TJSJSON.parseObject(SampleObjectData); // JS: Funcționează ca și cum ar fi definit nativ în JS asm console.log('JS: '+JSON.stringify(WC_Object)); console.log('JS: '+Object.keys(WC_Object).lungime); Sfârşit; // PAS: Convertirea într-un TJSONObject prin Strings funcționează ca înainte console.log('PAS: '+(TJSONObject.parseJSONValue(TJSJSON.stringify(WC_Object)) ca TJSONObject).ToString); console.log('PAS: '+IntToStr((TJSONObject.parseJSONValue(TJSJSON.stringify(WC_Object)) ca TJSONObject).Count)); Sfârşit; Ieșire console.log: JS: {"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"} JS: 5 PAS: {"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"} PAS: 5
27: Treceți de la PAS la JS sau WC.
Și aici, poate ați creat ceva JSON într-o aplicație VCL folosind varianta TJSONObject (PAS) și apoi ați descoperit că doriți să îl utilizați direct în JavaScript sau cu alt cod care folosește varianta WC. Interesant, dacă scoateți PAS TJSONObject în timp ce utilizați JS, veți vedea structura de bază și câteva indicii despre motivul pentru care este mai costisitor de utilizat:
{"fjv":{"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"},"fjo$1 ":{"apple":"fructe","banana":"fructe","portocale":"fructe","morcov":"legume","cartofi":"legume"},"FMembers":{" FList":{"FList":[],"FCount":0,"FCacity":0}}}
Dar oferă și un indiciu despre cum să îl utilizați în JS - doar referiți la „fjv” și veți avea JSON. Conversia prin șiruri este posibilă și aici, dar există o proprietate JSObject convenabilă care face parte din TJSONObject, ceea ce face conversia la varianta WC aproape la fel de ușoară.
procedura TForm1.WebButton1Click(Expeditor: TObject); var PAS_Object: TJSONObject; const SampleObjectData = '{"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"}'; ÎNCEPE PAS_Object := TJSONObject.parseJSONValue(SampleObjectData) ca TJSONObject; // JS asm console.log('JS: '+JSON.stringify(PAS_Object['fjv'])); console.log('JS: '+Object.keys(PAS_Object['fjv']).lungime); Sfârşit; // TOALETA console.log('WC: '+TJSJSON.stringify(PAS_Object.JSObject)); console.log('WC: '+IntToStr(lungime(TJSObject.keys(PAS_Object.JSObject)))); Sfârşit; Ieșire console.log: JS: {"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"} JS: 5 WC: {"apple":"fructe","banana":"fructe","orange":"fructe","morcov":"legume","cartofi":"legume"} WC: 5
28: FireDAC la JSON (simplu).
JSON este adesea folosit pentru a conține seturi de date tradiționale - coloane și rânduri de date cu care probabil suntem mai obișnuiți. În acest exemplu, vom lua un exemplu de set de date și vom arăta cum arată când este convertit în JSON. FireDAC este o alegere populară, dar orice altă bază de date are probabil un mecanism similar pentru a trece de la o interogare sau tabel sau un fel de set de date într-o reprezentare JSON. Aici, se presupune că acest cod rulează ca parte a unei aplicații VCL, deoarece FireDAC în sine nu este (în întregime?) disponibil direct în TMS WEB Core. În acest caz, folosim metoda BatchMove, în care doar datele în sine sunt incluse în JSON. Eșantionul de date este doar ceva pe care l-am extras direct dintr-un proiect. Nimic deosebit de interesant sau de proprietate, doar o mână de coloane și câteva înregistrări care să arate cum arată.
procedura TMyService.GetJSONData(ParametersOfSomeKind: String):TStream; var clientDB: TFDConnection; qry: TFDQuery; bm: TFDBatchMove; bw: TFDBatchMoveJSONWriter; br: TFDBatchMoveDataSetReader; ÎNCEPE Rezultat := TMemoryStream.Create; // Un fel de conexiune la baza de date clientDB := TFDConnection.Create(nil); clientDB.ConnectionDefName := SomeDatabaseConnection; clientDB.Connected := Adevărat; // Un fel de interogare qry.Conection := clientDB; qry.SQL.Text := 'selectați * din DATAMARK.LUTS;'; qry.Deschis; // Convertiți interogarea în JSON simplificat bm := TFDBatchMove.Create(nil); bw := TFDBatchMoveJSONWriter.Create(nil); br := TFDBatchMoveDataSetReader.Create(nil); încerca br.Dataset := qry; bw.Stream := Rezultat; bm.Reader := br; bm.Writer := bw; bm.Execute; in cele din urma br.Free; bw.Free; bm.Free; Sfârşit; // Curățare după aceea qry.Free; clientDB.Connected := False; clientDB.Free; Sfârşit;
JSON rezultat ar putea arăta cam așa.
[{"ID":1,"LOOKUP":0,"SORTORDER":0,"RESPONSE":"Administrație","MODIFICATOR":"ASIMARD","MODIFIED":"2021-11-17T19:42:34 ","GROUPTYPE":16},{"ID":2,"LOOKUP":1,"SORTORDER":1,"RESPONSE":"Labour","MODIFIER":"ASIMARD","MODIFIED":"2021 -11-17T19:47:52","GROUPTYPE":16},{"ID":3,"LOOKUP":2,"SORTORDER":2,"RESPONSE":"IT","MODIFICATOR":"ASIMARD ","MODIFICAT":"2021-11-17T19:42:56","GROUPTYPE":16}]
Dacă îl rulăm printr-un formatator JSON, devine puțin mai lizibil.
[
{
„ID”: 1,
„CĂUTARE”: 0,
„SORTARE”: 0,
„RESPONSE”: „Administrație”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:34”,
„TIP DE GRUP”: 16
},
{
„ID”: 2,
„CĂUTARE”: 1,
„SORTOR”: 1,
„RĂSPUNS”: „Munca de muncă”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:47:52”,
„TIP DE GRUP”: 16
},
{
„ID”: 3,
„CĂUTARE”: 2,
„SORTOR”: 2,
„RĂSPUNS”: „IT”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:56”,
„TIP DE GRUP”: 16
}
]
Acest lucru nu este teribil de eficient în ceea ce privește stocarea - o mulțime de text care se repetă, așa ceva. Deci, nu ați dori cu adevărat să faceți toate lucrările bazei de date exclusiv în JSON. Cu toate acestea, este compact în sensul că nu există nimic străin aici - Cheile reprezintă coloanele și Valorile reprezintă datele. Este un Array, așa că este ușor să ne dăm seama câte înregistrări există și utilizează câteva dintre tipurile de date JSON acceptate - Strings and Numbers. Există o dată/oră codificată în varianta mea preferată de ISO8601.
Un set de date formatat în acest fel în JSON este destul de util așa cum este. Chiar dacă nu știți în mod specific care sunt tipurile de câmpuri ale bazei de date, aveți o idee destul de bună. ID, LOOKUP, SORTORDER și GROUPTYPE par a fi numere întregi. RESPONSE și MODIFIER par a fi șiruri. Și MODIFIED pare a fi un marcaj de timp. Facem presupuneri aici, dar uneori este suficient, iar uneori nu este. Dar este simplu, așa că are asta pentru asta. Există multe situații în care ați putea să luați aceste date și să rulați cu ele fără alte considerații.
29 : FireDAC la JSON (Complex).
Similar cu cele de mai sus, exportăm un set de date în JSON. În acest caz, FireDAC are propria metodă de export în JSON, care include și metadate care descriu tipurile de date ale fiecărei coloane și o serie de alte lucruri care sunt probabil mai puțin interesante, dar utile dacă se deplasează între comenzile FireDAC în medii diferite.
procedura TMyService.GetJSONData(ParametersOfSomeKind: String):TStream; var clientDB: TFDConnection; qry: TFDQuery; ÎNCEPE Rezultat := TMemoryStream.Create; // Un fel de conexiune la baza de date clientDB := TFDConnection.Create(nil); clientDB.ConnectionDefName := SomeDatabaseConnection; clientDB.Connected := Adevărat; // Un fel de interogare qry.Conection := clientDB; qry.SQL.Text := 'selectați * din eșantion;'; qry.Deschis; // Convertiți interogarea în JSON FireDAC qry.SaveToStream(Rezultat, sfJSON); // Curățare după aceea qry.Free; clientDB.Connected := False; clientDB.Free; Sfârşit;
Nu prea multe de privit însă.
{"FDBS":{"Version":15,"Manager":{"UpdatesRegistry":true,"TableList":[{"class":"Tabel","Name":"DATAMARK.LUTS","SourceName" :"DATAMARK.LUTS","SourceID":1,"TabID":0,"EnforceConstraints":false,"MinimumCapacity":50,"ColumnList":[{"class":"Column","Name":" ID","SourceName":"ID","SourceID":1,"DataType":"Int64","Precision":19,"Searchable":true,"Base":true,"OInUpdate":true," OInWhere":true,"OriginColName":"ID","SourcePrecision":19},{"class":"Column","Name":"LOOKUP","SourceName":"LOOKUP","SourceID":2 ,"DataType":"Int32","Precision":10,"Searchable":true,"Base":true,"OInUpdate":true,"OInWhere":true,"OInKey":true,"OriginColName":" LOOKUP","SourcePrecision":10},{"class":"Column","Name":"SORTORDER","SourceName":"SORTORDER","SourceID":3,"DataType":"Int32"," Precision":10,"Searchable":true,"Base":true,"OInUpdate":true,"OInWhere":true,"OriginColName":"SORTORDER","SourcePrecision":10},{"class":" Column","Name":"RESPONSE","SourceName":"RESPONSE","SourceID":4,"DataType":"AnsiString","Size":200,"Searchable":true," Base":true,"OInUpdate":true,"OInWhere":true,"OriginColName":"RESPONSE","SourcePrecision":200,"SourceSize":200},{"class":"Column","Name" :"DESCRIPTION","SourceName":"DESCRIPTION","SourceID":5,"DataType":"AnsiString","Size":250,"Searchable":true,"AllowNull":true,"Base":true ,"OAllowNull":true,"OInUpdate":true,"OInWhere":true,"OriginColName":"DESCRIPTION","SourcePrecision":250,"SourceSize":250},{"class":"Column"," Name":"MODIFIER","SourceName":"MODIFIER","SourceID":6,"DataType":"AnsiString","Size":32,"Searchable":true,"FixedLen":true,"Base" :true,"OInUpdate":true,"OInWhere":true,"OriginColName":"MODIFICATOR","SourcePrecision":32,"SourceSize":32},{"class":"Column","Name":" MODIFIED","SourceName":"MODIFIED","SourceID":7,"DataType":"DateTimeStamp","Searchable":true,"AllowNull":true,"Base":true,"OAllowNull":true," OInUpdate":true,"OInWhere":true,"OriginColName":"MODIFIED","SourcePrecision":26},{"class":"Column","Name":"GROUPTYPE","SourceName":"GROUPTYPE" ,"SourceID":8,"DataType":"Int32","Precision":10, "Searchable":true,"Base":true,"OInUpdate":true,"OInWhere":true,"OInKey":true,"OriginColName":"GROUPTYPE","SourcePrecision":10}],"ConstraintList": [],"ViewList":[],"RowList":[{"RowID":0,"Original":{"ID":1,"LOOKUP":0,"SORTORDER":0,"RESPONSE":" Administrare","MODIFIER":"ASIMARD","MODIFIED":"2021-11-17T19:42:34","GROUPTYPE":16}},{"RowID":1,"Original":{"ID" :2,"LOOKUP":1,"SORTORDER":1,"RESPONSE":"Labour","MODIFIER":"ASIMARD","MODIFIED":"2021-11-17T19:47:52","GROUPTYPE" :16}},{"RowID":2,"Original":{"ID":3,"LOOKUP":2,"SORTORDER":2,"RESPONSE":"IT","MODIFICATOR":"ASIMARD" ,"MODIFIED":"2021-11-17T19:42:56","GROUPTYPE":16}}]}],"RelationList":[],"UpdatesJournal":{"Changes":[]}}}}
Dacă îl rulăm printr-un formatator, este puțin mai ușor să vedem ce se întâmplă.
{
„FDBS”: {
„Versiune”: 15,
"Administrator": {
„UpdatesRegistry”: adevărat,
„TableList”: [
{
"class": "Tabel",
„Nume”: „DATAMARK.LUTS”,
„SourceName”: „DATAMARK.LUTS”,
„SourceID”: 1,
„TabID”: 0,
„EnforceConstraints”: fals,
„Capacitate minimă”: 50,
„ColumnList”: [
{
"class": "Coloană",
„Nume”: „ID”,
"SourceName": "ID",
„SourceID”: 1,
„DataType”: „Int64”,
„Precizie”: 19,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "ID",
„SourcePrecision”: 19
},
{
"class": "Coloană",
„Nume”: „CĂUTARE”,
"SourceName": "CAUTĂ",
„SourceID”: 2,
„DataType”: „Int32”,
„Precizie”: 10,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
„OInKey”: adevărat,
"OriginColName": "CAUTĂ",
„SourcePrecision”: 10
},
{
"class": "Coloană",
„Nume”: „SORTORDER”,
"SourceName": "SORTORDER",
„SourceID”: 3,
„DataType”: „Int32”,
„Precizie”: 10,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "SORTORDER",
„SourcePrecision”: 10
},
{
"class": "Coloană",
„Nume”: „RĂSPUNS”,
„SourceName”: „RESPONSE”,
„SourceID”: 4,
„DataType”: „AnsiString”,
„Dimensiune”: 200,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "RĂSPUNS",
„SourcePrecision”: 200,
„SourceSize”: 200
},
{
"class": "Coloană",
„Nume”: „DESCRIPTION”,
„SourceName”: „DESCRIPTION”,
„SourceID”: 5,
„DataType”: „AnsiString”,
„Dimensiune”: 250,
„Se poate căuta”: adevărat,
„AllowNull”: adevărat,
„Baza”: adevărat,
„OAllowNull”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "DESCRIPTION",
„SourcePrecision”: 250,
„SourceSize”: 250
},
{
"class": "Coloană",
„Nume”: „MODIFICATOR”,
„SourceName”: „MODIFICATOR”,
„SourceID”: 6,
„DataType”: „AnsiString”,
„Dimensiune”: 32,
„Se poate căuta”: adevărat,
„FixedLen”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "MODIFICATOR",
„SourcePrecision”: 32,
„SourceSize”: 32
},
{
"class": "Coloană",
„Nume”: „MODIFICAT”,
„SourceName”: „MODIFICAT”,
„SourceID”: 7,
"DataType": "DateTimeStamp",
„Se poate căuta”: adevărat,
„AllowNull”: adevărat,
„Baza”: adevărat,
„OAllowNull”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "MODIFICAT",
„SourcePrecision”: 26
},
{
"class": "Coloană",
„Nume”: „GROUPTYPE”,
„SourceName”: „GROUPTYPE”,
„SourceID”: 8,
„DataType”: „Int32”,
„Precizie”: 10,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
„OInKey”: adevărat,
„OriginColName”: „GROUPTYPE”,
„SourcePrecision”: 10
}
],
„ConstraintList”: [],
„ViewList”: [],
„RowList”: [
{
„RowID”: 0,
„Original”: {
„ID”: 1,
„CĂUTARE”: 0,
„SORTARE”: 0,
„RESPONSE”: „Administrație”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:34”,
„TIP DE GRUP”: 16
}
},
{
„RowID”: 1,
„Original”: {
„ID”: 2,
„CĂUTARE”: 1,
„SORTOR”: 1,
„RĂSPUNS”: „Munca de muncă”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:47:52”,
„TIP DE GRUP”: 16
}
},
{
„RowID”: 2,
„Original”: {
„ID”: 3,
„CĂUTARE”: 2,
„SORTOR”: 2,
„RĂSPUNS”: „IT”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:56”,
„TIP DE GRUP”: 16
}
}
]
}
],
„RelationList”: [],
„UpdatesJournal”: {
"Schimbări": []
}
}
}
}
Este mult teren de acoperit pentru aceleași trei înregistrări, dar nu există absolut nicio presupunere când vine vorba de tipurile de date pe care le folosim. Ceea ce căutăm aici. În mod curios, puteți vedea că există un câmp DESCRIERE în baza de date, dar probabil că nu avea date, așa că a fost exclus din înregistrările ulterioare. Exact genul de optimizare care are loc în culise și care va înnebuni o persoană!
30: JSON la TDataset (simplu).
Folosind JSON din varianta simplă, tot ceea ce am acoperit se aplică în ceea ce privește căutarea datelor sau sortarea sau orice altceva ar putea fi necesar. Adesea, unul dintre obiective este să introducem datele JSON într-un set TDataset local unde putem reveni cu bucurie la utilizarea tuturor instrumentelor noastre Delphi. În TMS WEB Core, una dintre modalitățile de a face acest lucru este utilizarea componentei TXDataWebDataSet . Puteți plasa unul dintre acestea într-un formular și apoi faceți dublu clic pe el pentru a afișa editorul familiar Fields în timp de proiectare. Când sunteți gata să încărcați date în setul de date, puteți apela SetJSONData cu o matrice JSON pentru a încărca setul de date. Există, de asemenea, familiarele TWebDataSet și TWebGrid. La momentul proiectării, ar putea arăta așa.
Pentru a introduce datele în setul de date, există o funcție SetJSONData (). Există câteva avertismente aici. În primul rând, datele trebuie să fie în formatul care corespunde definițiilor coloanei sau vor fi excluse. Coloanele DateTime trebuie să aibă un anumit format, altfel vor fi excluse. Lucruri de genul acela. Așa că este puțin dificil de rezolvat primele două ori. De asemenea, nu există prea multe în ceea ce privește feedback-ul atunci când primește date cu care nu știe ce să facă. Iată cum arată.
procedura TForm1.WebButton1Click(Expeditor: TObject); var WC_Object: TJSObject; const SampleObjectData = '[{"ID":1,"LOOKUP":0,"SORTORDER":0,"RESPONSE":"Administration","MODIFICATOR":"ASIMARD","MODIFIED":"2021-11-17T19: 42:34","GROUPTYPE":16},'+ '{"ID":2,"LOOKUP":1,"SORTORDER":1,"RESPONSE":"Labour","MODIFIER":"ASIMARD","MODIFIED":"2021-11-17T19:47:52 ","GROUPTYPE":16},'+ '{"ID":3,"LOOKUP":2,"SORTORDER":2,"RESPONSE":"IT","MODIFICATOR":"ASIMARD","MODIFIED":"2021-11-17T19:42:56 ","GROUPTYPE":16}]'; ÎNCEPE WC_Object := TJSJSON.parseObject(SampleObjectData); XDataWebDataSet1.SetJSONData(WC_Object); XdataWebDataSet1.Open; Sfârşit;
Rețineți că am făcut toată munca grea în mediul de proiectare - ne dăm seama ce coloane dorim, cât de lățime trebuie să fie și așa mai departe. Dacă lucrurile merg conform planului, ești gata să mergi cu o grilă care ar putea arăta cam așa.
Și ai plecat și fugi!
31: JSON la TDataset (Complex).
Și în cele din urmă ajungem la problema pe care am întâlnit-o prima dată când am folosit TMS WEB Core. Ideea de bază este să folosesc aplicația TMS WEB Core ca client pentru, în cazul meu, un server XData. Serverul trimite date JSON din oricare dintre sutele de interogări diferite. Același lucru ar putea fi făcut pentru orice număr de alte surse de date, cum ar fi datele meteorologice și așa mai departe, care nu au nimic de-a face cu XData. JSON care ajunge apoi trebuie să fie gestionat de client.
Inițial, intenția a fost de a crea automat un TDataSet local cu datele, gata de funcționare. Cu toate acestea, fără a cunoaște toate definițiile câmpurilor din timp și fără a dori să le creăm pe toate în timpul proiectării, nu a existat nicio modalitate de a încărca datele folosind apelul SetJSONData (). Și în această etapă, datele JSON care vin au fost mai mult un mecanism de comunicare. Câmpurile sunt definite ca rezultat al interogării inițiale și vor trebui să fie cunoscute atunci când interfața de utilizare finală este prezentată utilizatorului, dar între ele chiar nu ne interesează ce se întâmplă - JSON este doar numele dat lui fluxul de date - conținutul acestuia nu prea contează în anumite etape.
funcția GetQueryData(Endpoint: String; Dataset: TXDataWebDataSet):Integer; var Conexiune: TXDataWebConnection; Client: TXDataWebClient; Răspuns: TXDataClientResponse; JFDBS: TJSObject; JManager: TJSObject; JTableList: TJSObject; JColumnList: TJSArray; JRowList: TJSArray; StringFields: Matrice de TStringField; IntegerFields: Matrice de TIntegerField; // Probabil mai aveți nevoie de câteva tipuri de date aici i: întreg; ÎNCEPE // Valoare pentru a indica că solicitarea a fost nereușită Rezultat := -1; // Configurați conexiunea la XData Server Conn := TXDataWebConnection.Create(nil); Conn.URL := DM1.CarnivalCoreServer; // ar putea fi, de asemenea, un parametru Conn.OnRequest := PopulateJWT; // Vezi mai jos Client := TXDataWebClient.Create(nil); Client.Connection := Conn; await(Conn.OpenAsync); // Faceți cererea // Probabil să aibă o altă versiune a funcției care include mai mulți parametri de punct final încerca Răspuns:= await(Client.RawInvokeAsync(Endpoint, ['FireDAC'])); cu exceptia on Error: Exception do ÎNCEPE Client.Free; Conn.Free; console.log('...ceva este în neregulă...'); Ieșire; Sfârşit; Sfârşit; // Procesează JSON-ul specific FireDAC care a fost returnat JFDBS := TJSObject(TJSObject(TJSJson.Parse(string(Response.Result)))['FDBS']); JManager := TJSObject(JFDBS['Manager']); JTableList := TJSObject(TJSArray(JManager['TableList'])[0]); JColumnList := TJSArray(JTableList['ColumnList']); JRowList := TJSArray(JTableList['RowList']); // Nu vreau cu adevărat „Original” în numele câmpurilor, așa că mai întâi să-l eliminăm din JSON // Probabil un one-liner mai bun, dar asta pare să funcționeze pentru i := 0 la JRowList.Length - 1 do JRowList.Elements[i] := TJSObject(JRowList.Elements[i])['Original']; // Presupunem că parametrul Dataset este nou creat și gol. // Mai întâi, adăugați toate câmpurile din JSON // NOTĂ: Foarte probabil, aici trebuie adăugate mai multe tipuri de date pentru i := 0 la JColumnList.Length-1 do ÎNCEPE dacă (String(TJSObject(JColumnList.Elements[i])['DataType']) = 'AnsiString') atunci ÎNCEPE // NOTĂ: Diferite tipuri de date pot avea nevoie de un set de valori diferite (de exemplu: Dimensiune pentru șiruri) SetLength(StringFields, Length(StringFields) + 1); StringFields[Lungime(StringFields)-1] := TStringField.Create(Dataset); StringFields[Length(StringFields)-1].FieldName := String(TJSObject(JColumnList.Elements[i])['Name']); StringFields[Length(StringFields)-1].Size := Integer(TJSObject(JColumnList.Elements[i])['Size']); StringFields[Length(StringFields)-1].Dataset := Dataset; Sfârşit else if (String(TJSObject(JColumnList.Elements[i])['DataType']) = 'Int32') atunci ÎNCEPE SetLength(IntegerFields, Length(IntegerFields) + 1); IntegerFields[Lungime(IntegerFields)-1] := TIntegerField.Create(Dataset); IntegerFields[Lungime(IntegerFields)-1].FieldName := String(TJSObject(JColumnList.Elements[i])['Nume']); IntegerFields[Length(IntegerFields)-1].Dataset := Dataset; Sfârşit altfel ÎNCEPE console.log('EROARE: Câmpul ['+String(TJSObject(JColumnList.Elements[i])['Nume'])+'] are un tip de date neașteptat ['+String(TJSObject(JColumnList.Elements[i])[ 'DataType'])+']'); Sfârşit; Sfârşit; // Adăugați datele și returnați setul de date așa cum a fost deschis Dataset.SetJSONData(JRowList); Dataset.Open; // Doar pentru distractie Rezultat := Dataset.RecordCount; // Nu există chestii de eliberare a setului de date deoarece setul de date a fost creat de apelant și // toate câmpurile create au fost create cu acel set de date ca părinte Client.Free; Conn.Free; Sfârşit;
Pentru mine, partea misterioasă a fost întotdeauna aceasta:
// Procesează JSON-ul specific FireDAC care a fost returnat
JFDBS := TJSObject(TJSObject(TJSJson.Parse(string(Response.Result)))['FDBS']);
JManager := TJSObject(JFDBS['Manager']);
JTableList := TJSObject(TJSArray(JManager['TableList'])[0]);
JColumnList := TJSArray(JTableList['ColumnList']);
JRowList := TJSArray(JTableList['RowList']);
// Nu vreau cu adevărat „Original” în numele câmpurilor, așa că mai întâi să-l eliminăm din JSON
// Probabil un one-liner mai bun, dar asta pare să funcționeze
pentru i := 0 la JRowList.Length - 1 do
JRowList.Elements[i] := TJSObject(JRowList.Elements[i])['Original'];
JFDBS := TJSObject(TJSObject(TJSJson.Parse(string(Response.Result)))['FDBS']);
Aceasta se încarcă inițial JSON (Response.Result vine direct de la server) și se elimină învelișul exterior cel mai de sus, „FDBS”.
JManager := TJSObject(JFDBS['Manager']);
Aceasta elimină un alt înveliș exterior, „Manager”.
JTableList := TJSObject(TJSArray(JManager['TableList'])[0]);
Acest lucru elimină un alt înveliș exterior, „TableList”, dar încă mai are destule lucruri transportate.
{
"class": "Tabel",
„Nume”: „DATAMARK.LUTS”,
„SourceName”: „DATAMARK.LUTS”,
„SourceID”: 1,
„TabID”: 0,
„EnforceConstraints”: fals,
„Capacitate minimă”: 50,
„ColumnList”: [
{
"class": "Coloană",
„Nume”: „ID”,
"SourceName": "ID",
„SourceID”: 1,
„DataType”: „Int64”,
„Precizie”: 19,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "ID",
„SourcePrecision”: 19
},
{
"class": "Coloană",
„Nume”: „CĂUTARE”,
"SourceName": "CAUTĂ",
„SourceID”: 2,
„DataType”: „Int32”,
„Precizie”: 10,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
„OInKey”: adevărat,
"OriginColName": "CAUTĂ",
„SourcePrecision”: 10
},
{
"class": "Coloană",
„Nume”: „SORTORDER”,
"SourceName": "SORTORDER",
„SourceID”: 3,
„DataType”: „Int32”,
„Precizie”: 10,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "SORTORDER",
„SourcePrecision”: 10
},
{
"class": "Coloană",
„Nume”: „RĂSPUNS”,
"SourceName": "RESPONSE",
„SourceID”: 4,
„DataType”: „AnsiString”,
„Dimensiune”: 200,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "RĂSPUNS",
„SourcePrecision”: 200,
„SourceSize”: 200
},
{
"class": "Coloană",
„Nume”: „DESCRIPTION”,
„SourceName”: „DESCRIPTION”,
„SourceID”: 5,
„DataType”: „AnsiString”,
„Dimensiune”: 250,
„Se poate căuta”: adevărat,
„AllowNull”: adevărat,
„Baza”: adevărat,
„OAllowNull”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "DESCRIPTION",
„SourcePrecision”: 250,
„SourceSize”: 250
},
{
"class": "Coloană",
„Nume”: „MODIFICATOR”,
„SourceName”: „MODIFICATOR”,
„SourceID”: 6,
„DataType”: „AnsiString”,
„Dimensiune”: 32,
„Se poate căuta”: adevărat,
„FixedLen”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "MODIFICATOR",
„SourcePrecision”: 32,
„SourceSize”: 32
},
{
"class": "Coloană",
„Nume”: „MODIFICAT”,
„SourceName”: „MODIFICAT”,
„SourceID”: 7,
"DataType": "DateTimeStamp",
„Se poate căuta”: adevărat,
„AllowNull”: adevărat,
„Baza”: adevărat,
„OAllowNull”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
"OriginColName": "MODIFICAT",
„SourcePrecision”: 26
},
{
"class": "Coloană",
„Nume”: „GROUPTYPE”,
„SourceName”: „GROUPTYPE”,
„SourceID”: 8,
„DataType”: „Int32”,
„Precizie”: 10,
„Se poate căuta”: adevărat,
„Baza”: adevărat,
„OInUpdate”: adevărat,
„OInWhere”: adevărat,
„OInKey”: adevărat,
„OriginColName”: „GROUPTYPE”,
„SourcePrecision”: 10
}
],
„ConstraintList”: [],
„ViewList”: [],
„RowList”: [
{
„RowID”: 0,
„Original”: {
„ID”: 1,
„CĂUTARE”: 0,
„SORTARE”: 0,
„RESPONSE”: „Administrație”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:34”,
„TIP DE GRUP”: 16
}
},
{
„RowID”: 1,
„Original”: {
„ID”: 2,
„CĂUTARE”: 1,
„SORTOR”: 1,
„RĂSPUNS”: „Munca de muncă”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:47:52”,
„TIP DE GRUP”: 16
}
},
{
„RowID”: 2,
„Original”: {
„ID”: 3,
„CĂUTARE”: 2,
„SORTOR”: 2,
„RĂSPUNS”: „IT”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:56”,
„TIP DE GRUP”: 16
}
}
]
}
O mulțime de lucruri acolo de care nu avem nevoie. Vom ajunge la asta într-o clipă. Acum ne trecem la definițiile coloanelor.
JColumnList := TJSArray(JTableList['ColumnList']);
JRowList := TJSArray(JTableList['RowList']);
Acum avem de-a face doar cu secțiunea „RowList”, care este ceea ce avem nevoie pentru ca SetJSONData să-și facă treaba. Acesta arată ca următorul. Similar cu simplul JSON, care este, desigur, ceea ce căutăm în ceea ce privește datele.
[
{
„RowID”: 0,
„Original”: {
„ID”: 1,
„CĂUTARE”: 0,
„SORTARE”: 0,
„RESPONSE”: „Administrație”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:34”,
„TIP DE GRUP”: 16
}
},
{
„RowID”: 1,
„Original”: {
„ID”: 2,
„CĂUTARE”: 1,
„SORTOR”: 1,
„RĂSPUNS”: „Munca de muncă”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:47:52”,
„TIP DE GRUP”: 16
}
},
{
„RowID”: 2,
„Original”: {
„ID”: 3,
„CĂUTARE”: 2,
„SORTOR”: 2,
„RĂSPUNS”: „IT”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:56”,
„TIP DE GRUP”: 16
}
}
]
// Nu vreau cu adevărat „Original” în numele câmpurilor, așa că mai întâi să-l eliminăm din JSON // Probabil un one-liner mai bun, dar asta pare să funcționeze pentru i := 0 la JRowList.Length - 1 do JRowList.Elements[i] := TJSObject(JRowList.Elements[i])['Original'];
Ceea ce duce la aceasta, forma finală care este importată prin SetJSONData:
[
{
„ID”: 1,
„CĂUTARE”: 0,
„SORTARE”: 0,
„RESPONSE”: „Administrație”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:34”,
„TIP DE GRUP”: 16
},
{
„ID”: 2,
„CĂUTARE”: 1,
„SORTOR”: 1,
„RĂSPUNS”: „Munca de muncă”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:47:52”,
„TIP DE GRUP”: 16
},
{
„ID”: 3,
„CĂUTARE”: 2,
„SORTOR”: 2,
„RĂSPUNS”: „IT”,
„MODIFICATOR”: „ASIMARD”,
„MODIFICAT”: „2021-11-17T19:42:56”,
„TIP DE GRUP”: 16
}
]
// Presupunem că parametrul Dataset este nou creat și gol.
// Mai întâi, adăugați toate câmpurile din JSON
// NOTĂ: Foarte probabil, aici trebuie adăugate mai multe tipuri de date
pentru i := 0 la JColumnList.Length-1 do
ÎNCEPE
dacă (String(TJSObject(JColumnList.Elements[i])['DataType']) = 'AnsiString') atunci
ÎNCEPE
// NOTĂ: Diferite tipuri de date pot avea nevoie de un set de valori diferite (de exemplu: Dimensiune pentru șiruri)
SetLength(StringFields, Length(StringFields) + 1);
StringFields[Lungime(StringFields)-1] := TStringField.Create(Dataset);
StringFields[Length(StringFields)-1].FieldName := String(TJSObject(JColumnList.Elements[i])['Name']);
StringFields[Length(StringFields)-1].Size := Integer(TJSObject(JColumnList.Elements[i])['Size']);
StringFields[Length(StringFields)-1].Dataset := Dataset;
Sfârşit
else if (String(TJSObject(JColumnList.Elements[i])['DataType']) = 'Int32') atunci
ÎNCEPE
SetLength(IntegerFields, Length(IntegerFields) + 1);
IntegerFields[Lungime(IntegerFields)-1] := TIntegerField.Create(Dataset);
IntegerFields[Lungime(IntegerFields)-1].FieldName := String(TJSObject(JColumnList.Elements[i])['Nume']);
IntegerFields[Length(IntegerFields)-1].Dataset := Dataset;
Sfârşit
altfel
ÎNCEPE
console.log('EROARE: Câmpul ['+String(TJSObject(JColumnList.Elements[i])['Nume'])+'] are un tip de date neașteptat ['+String(TJSObject(JColumnList.Elements[i])[ 'DataType'])+']');
Sfârşit;
Sfârşit;
Acum nu este atât de misterios să vedem ce se întâmplă, tipurile de date din coloană fiind extrase din JColumnList și, acolo unde este necesar, informații suplimentare despre tipurile de date. Deoarece acesta a fost scris inițial, a fost extins pentru a accepta câteva tipuri de date pe care le folosesc în mod regulat, dar să fii atent la mesajul „tip de date neașteptate” din console.log este o idee bună.
Concluzie.
Bine. Cam asta o acoperă. Cred că orice întrebări ulterioare care sunt de natură tehnică, îmbunătățiri sugerate pentru blocurile individuale de cod și așa mai departe ar trebui probabil tratate prin postări din Centrul de asistență TMS, unde avem puțin mai mult control asupra formatării și răspunsurilor în fire și curând. Dacă acesta se dovedește a fi un subiect popular, atunci crearea unui depozit GitHub pentru acesta poate avea, de asemenea, sens.
Și nu mă îndoiesc deloc că există îmbunătățiri care pot fi găsite în multe dintre aceste exemple. Mai mult de jumătate din secțiuni au fost scrise doar pentru acest articol, în special variantele WC. Deoarece sunt relativ nou în abordarea WC, este foarte probabil să fi trecut cu vederea sau să fi înțeles greșit unele dintre opțiunile disponibile. Acest lucru a fost scris și cu TMS WEB Core 1.9.8.3. Fără îndoială, versiunile viitoare ale TMS WEB Core și proiectul pas2js de bază vor introduce modificări și perfecționări care vor face posibile îmbunătățiri care ar putea să nu fie posibile astăzi. TJSONObject îi lipsesc câteva metode, de exemplu. JavaScript în sine a evoluat și va continua să evolueze, așa că sunt la fel de probabile îmbunătățiri în timp.
Andrew Simard.