Tehnični članak

Veljavnost sheme polj Pivot XLSX v Delphiju s HotXLS

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>
XML pivotField v HotXLS pred in po v2.384.33, kjer izmišljena vrednost osi rowAxis in prazen element items kršita CT_PivotField, dokler pisatelj ne izda žetonov ST_Axis, kot je axisRow, s pravimi vnosi item, ohranjeno zastavico skritosti in zaključno privzeto subtotalo, ki jih shema sprejme
Popustljivi bralec je sprejel obe črkovanji, zato je vsak round-trip uspel, medtem ko je datoteka podrla vsak strog preizkus sheme — zapišite le štiri žetone ST_Axis in pustite CT_Items nositi vsaj en item

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)

Anatomija seznama itemov vrtilne tabele v HotXLS, kjer vnosi podatkovnih itemov sledijo zaključnim itemom delnih vsot, izpeljanim iz TXLSPivotField.Subtotals, kot sta t=default in t=avg, preštetim v items count, z razpisano pastjo poimenovanja xlpsCount na countA in xlpsCountNums na count
Polja iz AddPivotTable se začnejo s prazno množico Subtotals, kar zapiše defaultSubtotal=0 in brez zaključnega itema — zahtevajte funkcije, ki jih hočete, pisatelj pa izpelje en item na funkcijo v štetje
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

Sidro vrtilne tabele HotXLS na vrstici 70001 proti stropu 16 bitov, kjer sta bila FirstRow in LastRow shranjena kot Word in stisnjena z Min proti High(Word) pri 65535, kar je odrezalo vrtilne tabele na črti ali pod njo, dokler v2.384.37 ni preselilo modela na polja Integer z vrnitvijo nil izven mreže
Polja Word so bila ostanek BIFF8 SxView v formatu, katerega listi tečejo do 1048576 vrstic — sidro čez vrstico 65536 se je zavilo v obseg 16 bitov in izgubilo svojo vrtilno tabelo ob shranjevanju
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