Tehnički članak

Valjanost sheme XLSX pivot polja u Delphiju uz HotXLS

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>
HotXLS pivotField XML prije i poslije v2.384.33 gdje izmišljena axis vrijednost rowAxis i prazan items element krše CT_PivotField sve dok writer ne ispisuje ST_Axis tokene poput axisRow sa stvarnim item unosima, sačuvanim hidden flagom i završnim default subtotalom koji shema prima
Blag čitač prihvaćao je oba pravopisa, pa je svaki round trip prolazio dok je datoteka padala na svaku strogiju shema provjeru — ispisujte samo četiri ST_Axis tokena i pustite CT_Items da nosi barem jedan item

Š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)

Anatomija popisa pivot itema u HotXLS-u gdje unose data itema slijede završni subtotal itemi izvedeni iz TXLSPivotField.Subtotals poput t=default i t=avg i brojani u items count, uz razjašnjenu zamku imenovanja xlpsCount u countA i xlpsCountNums u count
Polja iz AddPivotTable počinju s praznim Subtotals skupom, što zapisuje defaultSubtotal=0 i bez završnog itema — zatražite funkcije koje želite pa writer po funkciji izvodi jedan item u broj
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

HotXLS pivot sidro na retku 70001 naspram 16-bitnog stropa gdje su se FirstRow i LastRow spremali kao Word i stezali s Min prema High(Word) na 65535, odrezujući pivote na granici ili ispod nje sve dok v2.384.37 model nije prebacila na Integer polja s nil povratkom izvan grida
Word polja bila su BIFF8 SxView zaostatak u formatu čiji listovi idu do 1048576 redaka — sidro iza retka 65536 prelilo se u 16-bitni raspon i gubilo pivot pri spremanju
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