HotXLS zapisuje definicije vrtilnih tabel XLSX, katerih elementa pivotField in cacheField prestaneta shemo ECMA-376 Part 1 §18.10: atributi osi uporabljajo žetone ST_Axis axisRow, axisCol in axisPage, polja na območju vrednosti nosijo dataField="1", seznami itemov nikoli niso prazni, polja predpomnilnika pa shranjujejo številski numFmtId. Od v2.384.33 bralec spoštuje tudi privzete vrednosti sheme, ki jih je prej imel narobe
Napake za tem počiščenjem si delijo nelaskavo lastnost: nobena nikoli ni padla na preizkusu. HotXLS je zapisal vrtilno tabelo, HotXLS jo je prebral nazaj, vsako polje je pristalo na pravi osi, zbirka round-trip preizkusov pa je bila leta zelena. Težava je bila, da sta se pisatelj in bralec tiho dogovorila o zasebnem narečju. Vrtilna tabela, zgrajena iz Delphija, je zgledala v redu komponenti, ki jo je naredila, preizkus proti CT_PivotField in CT_CacheField pa je pokazal neveljavne žetone enumeracij, prazen element, ki ga shema prepoveduje, in zastavice, ki jih Excel pričakuje, a jih nikoli ni dobil. Če vrtilne tabele ustvarjate na strežniku in jih pošiljate ljudem, ki jih odprejo v Excelu ali jih dajo svojim razčlenjevalnikom, je edina pogodba, ki šteje, shema, ne pa kar koli, kar vaš bralec po naključju odpusti
Zakaj round-tripi HotXLS nikoli niso ujeli napačnih žetonov osi?
Round-tripi HotXLS nikoli niso ujeli napačnih žetonov osi, ker je bralec sprejel obe črkovanji. Stari XlsxPivotAxisAttr je izdajal axis="rowAxis", colAxis in pageAxis, kar se v angleščini naravno bere, v shemi pa ne obstaja; ST_Axis definira točno štiri vrednosti, axisRow, axisCol, axisPage in axisValues. Medtem je PivotAxisFromToken v lxPivotXml.pas ujel tako žeton sheme kot izmišljenega, zato je vsak samopreizkus uspel. Pisatelj zdaj izdaja le žetone sheme, bralec pa še naprej sprejema stara črkovanja, tako da se datoteke, shranjene s starejšimi različicami HotXLS, še vedno nalagajo z nedotaknjeno razporeditvijo
<!-- pred v2.384.33: neveljavna vrednost ST_Axis, prazen CT_Items -->
<pivotField axis="rowAxis" defaultSubtotal="1"><items count="0"></items></pivotField>
<!-- od v2.384.33 -->
<pivotField axis="axisRow" defaultSubtotal="1">
<items count="4"><item x="0"/><item x="1"/><item x="2" h="1"/><item t="default"/></items>
</pivotField>
Kaj zahteva CT_PivotField, kar je stari pisatelj preskočil?
CT_PivotField zahteva tri stvari, ki jih je stari BuildPivotTableXml izpustil ali imel narobe. Prvič, polje, združevano na območju vrednosti, mora to povedati na svoji lastni definiciji s dataField="1"; pisatelj to zastavico zdaj nastavi na vsakem polju, na katerega se sklicuje vnos v DataFields, ne le na seznamu <dataFields>. Drugič, CT_Items potrebuje vsaj en item, zato polje brez itemov ne dobi več praznega <items count="0">, celoten element pa se preprosto izpusti. Tretjič, vsak item obdrži svoje stanje: h="1" za skriti item (TXLSPivotItem.IsHidden) in sd="0" za strnjene podrobnosti (IsDetailHidden), oboje pa je stari pisatelj odvrgel ob vsakem shranjevanju
Subtilen del so zaključni itemi delnih vsot. Kadar ima polje iteme, Excel navaja enega dodatnega item na funkcijo delne vsote za podatkovnimi itemi, tipiziran s ST_ItemType: <item t="default"/> za samodejno delno vsoto, nato sum, countA, avg, max, min, product, count, stdDev, stdDevP, var in varP za izrecne. HotXLS te vnose izpelje iz TXLSPivotField.Subtotals ob shranjevanju in jih prešteje v items count. Polja, ustvarjena s AddPivotTable, se začnejo s prazno množico Subtotals, kar zapiše defaultSubtotal="0" in brez zaključnega itema, zato zahtevajte delne vsote izrecno, kadar jih poročilo potrebuje. Pomeni past poimenovanja: xlpsCount se preslika na countA (vsi vnosi) in xlpsCountNums na count (le številke)
uses
lxHandleX, lxPivot;
var
Book : TXLSXWorkbook;
Sheet : TXLSXWorksheet;
Pivot : TXLSPivotTable;
Region: TXLSPivotField;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('orders.xlsx');
Sheet := Book.Sheets[1]; // z osnovo 1, kot pogon XLS
Pivot := Sheet.AddPivotTable('Data!$A$1:$D$800', 3, 6, 'RegionTotals');
if Pivot = nil then
raise Exception.Create('Bad source range or anchor');
Region := Pivot.AddRowField('Region'); // nil, če takega polja ni
if Region <> nil then
Region.Subtotals := [xlpsDefault, xlpsAverage]; // -> t="default", t="avg"
Pivot.AddColumnField('Quarter');
Pivot.AddDataFieldByName('Revenue', xlpaSum); // označi Revenue z dataField="1"
Book.SaveAs('orders-pivot.xlsx');
finally
Book.Free;
end;
end;
Kako HotXLS zdaj bere iteme delnih vsot in privzete vrednosti sheme?
Bralnik HotXLS zdaj preskoči vsak item, katerega atribut t je prisoten in ni data, ker vnosi delnih vsot, skupnih vsot in prazni ne nosijo indeksa predpomnilnika. Pred v2.384.34 so se ti vnosi nalagali kot običajni itemi s CacheItemIndex na -1, zato je vrtilna tabela iz Excela prišla nazaj s fantomskimi člani, ki nikamor niso kazali, vsaka koda, ki je obšla Items, pa jih je morala odfiltrirati ročno. Ker pisatelj znova zgradi zaključne vnose iz Subtotals, je naloga bralca, da jih prevede v to množico, ne da bi jih obdržal kot podatke
Drugi popravek bralca zadeva atribute, ki jih ni. V shemi sta defaultSubtotal na CT_PivotField in containsString na CT_SharedItems privzeto true, Excel ju izpusti, kadar držita to privzeto vrednost. HotXLS je manjkajoči atribut prebral kot false, kar je pomenilo, da je vsaka vrtilna tabela, shranjena v Excelu, ob nalaganju tiho izgubila privzeto delno vsoto, preprosto tekstovno polje predpomnilnika pa je bilo razvrščeno kot mešano namesto nizno. To je zrcalna slika napake osi: pisatelj, ki vedno razpiše vsak atribut, nikoli ne vadi poti privzete vrednosti, tako da jo razkrijejo le datoteke drugega izdelovalca
Zakaj je bil numFmtId="General" neveljaven na poljih predpomnilnika?
Vrednost numFmtId="General" je bila neveljavna, ker je ST_NumFmtId nepredznačeno celo število, ne ime formata. Stari pisatelj predpomnilnika je ta niz trdo zakodiral na vsak cacheField, si sposoje ime, ki ga uporabniki vidijo v pogovornem oknu Oblikuj celice. HotXLS zdaj zapiše NumberFormat polja predpomnilnika kot številko, kar je 0 (vgrajeni format General), če ga ni kaj nastavilo. Strog razčlenjevalnik, ki tipizira atribute iz sheme, staro vrednost zavrne na mestu, in to je točno razred napak, ki se spremeni v pogovorno okno za popravilo; članek o pravilih OPC in označevanja za pozivom Excel k popravilu pokriva, kako se ta okna sprožijo
Zakaj so bile vrtilne tabele pod vrstico 65535 odrezane?
Vrtilne tabele XLSX, postavljene na vrstico 65536 ali pod njo, so bile odrezane, ker je skupni model vrtilnih tabel shranjeval FirstRow, LastRow, FirstHeaderRow, FirstDataRow in stolpčne dvojnike kot Word, koda za premik vrstic pa jih je stisnila s Min(.., High(Word)). To je ostanek zapisa BIFF8 SxView, kjer je 16 bitov dovolj, list XLSX pa teče do 1.048.576 vrstic. Od v2.384.37 so te lastnosti na TXLSPivotTable tipa Integer, stiski so odstranjene, vrednosti pa zoži le pisatelj BIFF8. TXLSXWorksheet.AddPivotTable in AddPivotTableCopy zdaj vrneta nil za sidro izven 1..1048576 krat 1..16384 ali za kopijo, katere obseg bi ušel z mreže
var
Pivot: TXLSPivotTable;
Check: TXLSXWorkbook;
begin
// Vrstica 70001 se je prej zavila v obseg 16 bitov; zdaj preživi shranjevanje in nalaganje
Pivot := Sheet.AddPivotTable('Data!$A$1:$D$800', 70001, 1, 'LateTotals');
if Pivot = nil then
Exit; // sidro izven lista ali nerazrešljiv izvorni obseg
Pivot.AddRowField('Region');
Pivot.AddDataFieldByName('Revenue', xlpaSum);
Book.SaveAs('late.xlsx');
Check := TXLSXWorkbook.Create;
try
Check.Open('late.xlsx');
Pivot := Check.Sheets[1].PivotTables.FindByName('LateTotals');
Assert((Pivot <> nil) and (Pivot.FirstRow = 70001));
finally
Check.Free;
end;
end;
Klasični pogon XLS je pravi popravek dobil v v2.384.38. Njegov model je shranjeval surove vrednosti SxView in DConRef z osnovo 0 in podajal sidra AddPivotTable naravnost skozi, dokumentacija, demonstracije in pogon XLSX pa so vsi uporabljali celice z osnovo 1, kot je Cells[Row, Col]. Oba pogona zdaj hranita položaje z osnovo 1 v modelu, bralec BIFF8 doda 1, pisatelj pa odšteje 1 na meji zapisa, tako da mora koda, ki se je zasidrala na (0, 0), iti na (1, 1), ker klasični AddPivotTable zdaj vrne nil za sidro izven 1..65536 krat 1..256; nov klic zapiše iste bajte kot stari. Razporeditev zapisa sama se ni spremenila in je opisana v zapisih SX BIFF8 za klasičnimi vrtilnimi tabelami .xls
Preverjajte proti shemi, ne proti lastnemu bralcu
Lekcija se posploši čez vrtilne tabele: popustljivi bralec skriva kršitve pisatelja, tako da round-trip skozi lastno kodo dokaže skladnost, ne pravilnost. Vsaka napaka tu je preživela, ker sta strpna stran in pokvarjena stran živeli v isti knjižnici. Preizkusi, ki ta razred napak res ujamejo, so validacija sheme izdelanih delov, datoteke, izdelane v Excelu, podane skozi vašega bralca z atributi, izpuščenimi na privzetih vrednostih, in fixture, ki pripnejo točen žeton, ne razčlenjenega rezultata. Vrtilne tabele, ki jih grujete skozi API, vključno z izračunanimi polji, izračunanimi itemi in razporeditvami odstotka skupne vsote, prikazanimi v gradnji in osveževanju vrtilnih tabel XLSX z izračunanimi polji, dobijo popravljene XML brez spremembe kode, vrtilne tabele, naložene iz datotek Excel, pa še naprej predvajajo svoje izvirne dele, dokler jih ne spremenite
Vsi ti popravki so del trenutne komponente preglednic HotXLS za Delphi, ki iz Delphija in C++Builder bere in zapisuje XLS, XLSX in vrtilne tabele brez Excela ali COM avtomatizacije na stroju