HotXLS zapisuje definicije XLSX pivot tablica čiji se elementi pivotField i cacheField validiraju prema shemi ECMA-376 Part 1 §18.10: axis atributi koriste ST_Axis tokene axisRow, axisCol i axisPage, polja vrijednosnog područja nose dataField="1", popisi itema nikad nisu prazni, a cache polja spremaju numerički numFmtId. Od v2.384.33 čitač poštuje i zadane vrijednosti sheme koje je ranije krivo čitao
Greške iza ovog čišćenja dijele nelaskavu crtu: nijedna nikad nije pala na testu. HotXLS napisao je pivot, HotXLS ga pročitao natrag, svako polje sletjelo je na pravu osu, i round-trip suite bio je zelen godinama. Problem bio je da su se pisac i čitač tiho složili oko privatnog dijalekta. Pivot građen iz Delphija komponenti koja ga je napravila izgledao je u redu, dok je provjera prema CT_PivotField i CT_CacheField izbacivala nevažeće enumeracijske tokene, prazan element koji shema zabranjuje i flagove koje Excel očekuje a nikad nije dobio. Ako pivote generirate na serveru i šaljete ih ljudima koji ih otvaraju u Excelu ili ugrađuju u vlastite parsere, jedini ugovor koji se računa je shema, a ne što Vaš vlastiti čitač slučajno oprosti
Zašto HotXLS round tripovi nikad nisu uhvatili krive axis tokene?
HotXLS round tripovi nikad nisu uhvatili krive axis tokene jer je čitač prihvaćao oba pravopisa. Stari XlsxPivotAxisAttr ispisivao je axis="rowAxis", colAxis i pageAxis, što se prirodno čita na engleskom ali ne postoji u shemi; ST_Axis definira točno četiri vrijednosti, axisRow, axisCol, axisPage i axisValues. U međuvremenu PivotAxisFromToken u lxPivotXml.pas poklapao je i shema token i izmišljeni, pa je svaki self-test prolazio. Sadašnji writer ispisuje samo shema tokene, a čitač i dalje prihvaća stare pravopise da bi datoteke spremljene ranijim HotXLS verzijama i dalje loadale s netaknutim rasporedom
<!-- before v2.384.33: invalid ST_Axis value, empty CT_Items -->
<pivotField axis="rowAxis" defaultSubtotal="1"><items count="0"></items></pivotField>
<!-- since 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>
Što CT_PivotField traži, a stari writer preskakao?
CT_PivotField traži tri stvari koje je stari BuildPivotTableXml izostavljao ili krivo radio. Prvo, polje agregirano u vrijednosnom području mora to reći na vlastitoj definiciji s dataField="1"; writer taj flag sada postavlja na svako polje koje referencira unos u DataFields, a ne samo u popisu <dataFields>. Drugo, CT_Items treba barem jedan item, pa polje bez itema više ne dobiva prazan <items count="0"> nego se cijeli element jednostavno izostavlja. Treće, svaki item čuva svoje stanje: h="1" za skriveni item (TXLSPivotItem.IsHidden) i sd="0" za sažete detalje (IsDetailHidden), oboje što je stari writer odbacivao pri svakom spremanju
Suptilan dio su završni subtotal itemi. Kad polje ima iteme, Excel nakon data itema nabraja po jedan dodatni item po subtotal funkciji, tipiziran s ST_ItemType: <item t="default"/> za automatski subtotal, zatim sum, countA, avg, max, min, product, count, stdDev, stdDevP, var i varP za eksplicitne. HotXLS te unose izvodi iz TXLSPivotField.Subtotals u trenutku spremanja i broji ih u items count. Polja stvorena s AddPivotTable počinju s praznim Subtotals skupom, što zapisuje defaultSubtotal="0" i bez završnog itema, pa tražite subtotale eksplicitno kad izvještaj treba. Obratite pozornost na zamku u imenovanju: xlpsCount mapira se na countA (svi unosi), a xlpsCountNums na count (samo brojevi)
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]; // od 1, kao u XLS engineu
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 ako takvo polje ne postoji
if Region <> nil then
Region.Subtotals := [xlpsDefault, xlpsAverage]; // -> t="default", t="avg"
Pivot.AddColumnField('Quarter');
Pivot.AddDataFieldByName('Revenue', xlpaSum); // postavlja Revenue dataField="1"
Book.SaveAs('orders-pivot.xlsx');
finally
Book.Free;
end;
end;
Kako HotXLS sada čita subtotal iteme i zadane vrijednosti sheme?
HotXLS čitač sada preskače svaki item čiji t atribut postoji a nije data, jer subtotal, grand-total i prazni unosi nemaju cache indeks. Prije v2.384.34 ti su se unosi loadali kao obični itemi s CacheItemIndex postavljenim na -1, pa je pivot napravljen u Excelu vraćao s fantomskim članovima koji nigdje nisu pokazivali, a svaki kod koji je hodao po Items morao ih je isfiltrirati ručno. Budući da writer završne unose ponovno gradi iz Subtotals, posao čitača je da ih prevede u taj skup, a ne da ih čuva kao podatke
Drugi popravak čitača tiče se atributa koji su odsutni. U shemi defaultSubtotal na CT_PivotField i containsString na CT_SharedItems oba imaju zadanu vrijednost true, a Excel ih izostavlja kad nose tu zadanu vrijednost. HotXLS je odsutni atribut čitao kao false, što je značilo da svaki pivot spremljen u Excelu tiho gubi svoj zadani subtotal pri učitavanju, a obično tekstno cache polje klasificiralo se kao miješano umjesto kao string. To je zrcalna slika axis greške: writer koji uvijek ispisuje svaki atribut nikad ne vježba putanju zadane vrijednosti, pa je otkrivaju samo datoteke od drugog proizvođača
Zašto je numFmtId="General" bilo nevažeće na cache poljima?
Vrijednost numFmtId="General" bila je nevažeća jer je ST_NumFmtId nepredznačeni integer, a ne ime formata. Stari cache writer tu je string hardkodirao na svakom cacheField, posuđujući ime koje korisnici vide u Format Cells dijalogu. HotXLS sada NumberFormat cache polja zapisuje kao broj, što je 0 (ugrađeni General format) osim ako je nešto postavilo drugo. Strogi parser koji tipizira atribute iz sheme staru vrijednost odbacuje odmah, i to je točno ta klasa grešaka koja se pretvara u dijalog za popravak; članak o OPC i markup pravilima iza Excelova repair prompta pokriva kako se ti dijalozi okidaju
Zašto su pivot tablice ispod retka 65535 odrezane?
XLSX pivot tablice smještene na redak 65536 ili ispod odrezivale su se jer je zajednički pivot model spremao FirstRow, LastRow, FirstHeaderRow, FirstDataRow i stupčane ekvivalente kao Word, a kod za pomak redaka stezao ih je s Min(.., High(Word)). To je zaostatak BIFF8 SxView zapisa gdje 16 bita i jest dovoljno, ali XLSX list ide do 1.048.576 redaka. Od v2.384.37 ta svojstva na TXLSPivotTable su Integer, stege su nestale, i vrijednosti sužava samo BIFF8 writer. TXLSXWorksheet.AddPivotTable i AddPivotTableCopy sada vraćaju nil za sidro izvan 1..1048576 puta 1..16384, odnosno za kopiju čiji bi opseg izišao s grida
var
Pivot: TXLSPivotTable;
Check: TXLSXWorkbook;
begin
// Redak 70001 prelijevao se u 16-bitni raspon; sada preživi spremanje i učitavanje
Pivot := Sheet.AddPivotTable('Data!$A$1:$D$800', 70001, 1, 'LateTotals');
if Pivot = nil then
Exit; // sidro izvan lista ili razriješivi izvorni raspon
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 XLS engine dobio je odgovarajući popravak u v2.384.38. Njegov je model spremao sirove 0-bazirane vrijednosti SxView i DConRef i propuštao AddPivotTable sidra izravno, dok su dokumentacija, demo primjeri i XLSX engine koristili ćelije od 1 poput Cells[Row, Col]. Oba enginea sada drže pozicije od 1 u modelu, BIFF8 čitač dodaje 1 a writer oduzima 1 na granici zapisa, pa kod koji je sidrio na (0, 0) mora na (1, 1), jer klasični AddPivotTable sada vraća nil za sidro izvan 1..65536 puta 1..256; novi poziv zapisuje iste bajtove kao stari. Sam raspored zapisa nepromijenjen je i opisan u BIFF8 SX zapisima iza klasičnih .xls pivot tablica
Validirajte prema shemi, ne prema vlastitom čitaču
Lekcija se generalizira iza pivota: blag čitač skriva kršenja pisca, pa round trip kroz vlastiti kod dokazuje usklađenost, a ne ispravnost. Svaka greška ovdje preživjela je jer su toleratna i pokvarena strana živjele u istoj biblioteci. Provjere koje ovu klasu defekta stvarno hvataju su shema validacija generiranih dijelova, datoteke proizvedene u Excelu provučene kroz Vaš čitač s atributima izostavljenim na zadanima, i fixture koji prianjaju uz točan token a ne na parsirani rezultat. Pivote koje gradite kroz API, uključujući calculated fields, calculated items i percent-of-total rasporede prikazane u građenju i osvježavanju XLSX pivot tablica s calculated fields, dobivaju ispravljeni XML bez ikakve izmjene koda, dok pivote loadane iz Excelovih datoteka nastavljaju ponavljati izvorne dijelove dok ih ne izmijenite
Svi ovi popravci stižu u trenutnu HotXLS Delphi spreadsheet komponentu, koja iz Delphija i C++Buildera čita i zapisuje XLS, XLSX i pivot tablice bez Excela ili COM automationa na stroju