Teknik Makale

HotXLS ile Delphi'de XLSX Pivot Alanı Şema Geçerliliği

HotXLS, pivotField ve cacheField ögeleri ECMA-376 Part 1 §18.10 şemasına göre doğrulayan XLSX pivot tablo tanımları yazar: axis öznitelikleri ST_Axis tokenları axisRow, axisCol ve axisPage'i kullanır, değer alanındaki alanlar dataField="1" taşır, item listeleri hiçbir zaman boş değildir ve cache alanları sayısal bir numFmtId saklar. v2.384.33'ten beri okuyucu, eskiden yanlış yaptığı şema varsayılanlarını da uygular

Bu temizliğin ardındaki hatalar ortak bir utanç verici özellik taşır: hiçbiri bir testi hiç başarısızlığa uğratmadı. HotXLS bir pivot yazıyor, HotXLS geri okuyor, her alan doğru eksene düşüyor ve gidiş dönüş paketi yıllarca yeşil kalıyordu. Sorun, yazıcı ile okuyucunun sessizce özel bir lehçede anlaşmış olmasıydı. Delphi'den kurulan bir pivot, onu üreten bileşene güzel görünürken CT_PivotField ve CT_CacheField'e karşı yapılan bir denetim geçersiz sayım tokenları, şemanın yasakladığı boş bir öge ve Excel'in beklediği ama hiç almadığı bayraklar çıkarıyordu. Pivotları sunucuda üretip Excel'de açacak ya da kendi ayrıştırıcılarına verecek kişilere gönderiyorsanız, tek sayılan sözleşme şemadır; kendi okuyucunuzun neyi bağışladığı değil

HotXLS gidiş dönüşleri yanlış axis tokenlarını neden hiç yakalamadı?

HotXLS gidiş dönüşleri yanlış axis tokenlarını hiç yakalamadı, çünkü okuyucu her iki yazımı da kabul ediyordu. Eski XlsxPivotAxisAttr, İngilizceye doğal okunan ama şemada bulunmayan axis="rowAxis", colAxis ve pageAxis değerlerini basıyordu; ST_Axis tam olarak dört değer tanımlar: axisRow, axisCol, axisPage ve axisValues. Bu arada lxPivotXml.pas içindeki PivotAxisFromToken hem şema tokenını hem uyduruk olanı eşliyordu; böylece her kendi kendine test geçiyordu. Yazıcı artık yalnızca şema tokenlarını basıyor; okuyucu ise eski yazımları kabul etmeye devam eder, böylece önceki HotXLS sürümlerinin kaydettiği dosyalar yerleşimleri bozulmadan yüklenir

<!-- v2.384.33 öncesi: geçersiz ST_Axis değeri, boş CT_Items -->
<pivotField axis="rowAxis" defaultSubtotal="1"><items count="0"></items></pivotField>

<!-- v2.384.33'ten beri -->
<pivotField axis="axisRow" defaultSubtotal="1">
  <items count="4"><item x="0"/><item x="1"/><item x="2" h="1"/><item t="default"/></items>
</pivotField>
v2.384.33 öncesi ve sonrası HotXLS pivotField XML'i: uyduruk axis değeri rowAxis ile boş items ögesi, yazıcı axisRow gibi ST_Axis tokenlarını gerçek item girdileriyle, korunan hidden bayrağıyla ve şemanın kabul ettiği sonda default ara toplamıyla basana dek CT_PivotField'i ihlal eder
Hoşgörülü okuyucu her iki yazımı da kabul ediyordu; her gidiş dönüş geçerken dosya her katı şema denetimini kırıyordu — yalnızca dört ST_Axis tokenını yazın ve CT_Items'ın en az bir item taşımasına izin verin

CT_PivotField, eski yazıcının atladığı neyi gerektirir?

CT_PivotField, eski BuildPivotTableXml'in ya atladığı ya yanlış yaptığı üç şey gerektirir. Birincisi, değer alanında toplanan bir alan bunu kendi tanımında dataField="1" ile söylemelidir; yazıcı artık o bayrağı DataFields içindeki bir girdinin referans verdiği her alana koyar, yalnızca <dataFields> listesinde değil. İkincisi, CT_Items en az bir item gerektirir; itemsiz bir alan artık boş bir <items count="0"> almaz ve tüm öge hiç yazılmaz. Üçüncüsü, her item durumunu korur: gizli item için h="1" (TXLSPivotItem.IsHidden) ve kapatılmış ayrıntılar için sd="0" (IsDetailHidden); eski yazıcı ikisini de her kaydetmede düşürüyordu

İnce nokta, sondaki ara toplam itemlarıdır. Bir alanın itemları olduğunda Excel, veri itemlarının ardından her ara toplam fonksiyonu için bir ekstra item listeler; bunlar ST_ItemType ile tiplenir: otomatik ara toplam için <item t="default"/>, açık olanlar için sum, countA, avg, max, min, product, count, stdDev, stdDevP, var ve varP. HotXLS bu girdileri kaydetme anında TXLSPivotField.Subtotals'dan türetir ve items count'a sayar. AddPivotTable ile kurulan alanlar boş bir Subtotals kümesiyle başlar; bu, defaultSubtotal="0" ve sonda item olmadan yazar; rapor ara toplam istiyorsa onları açıkça isteyin. Adlandırma tuzağına dikkat: xlpsCount countA'ya (tüm girdiler), xlpsCountNums count'a (yalnızca sayılar) eşlenir

HotXLS pivot items listesi anatomisi: veri item girdilerini, TXLSPivotField.Subtotals'dan türetilen t=default ve t=avg gibi sonda ara toplam itemları izler ve bunlar items count'a sayılır; xlpsCount'un countA'ya, xlpsCountNums'un count'a eşleşen adlandırma tuzağı açıkça yazılıdır
AddPivotTable'dan gelen alanlar boş bir Subtotals kümesiyle başlar; bu defaultSubtotal=0 yazar ve sonda item koymaz — istediğiniz fonksiyonları söyleyin, yazıcı fonksiyon başına bir item türetip sayaca ekler
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];                  // 1 tabanlı, XLS motoru gibi
    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');    // böyle bir alan yokken nil
    if Region <> nil then
      Region.Subtotals := [xlpsDefault, xlpsAverage];  // -> t="default", t="avg"
    Pivot.AddColumnField('Quarter');
    Pivot.AddDataFieldByName('Revenue', xlpaSum);      // Revenue'a dataField="1" bayrağını koyar

    Book.SaveAs('orders-pivot.xlsx');
  finally
    Book.Free;
  end;
end;

HotXLS ara toplam itemlarını ve şema varsayılanlarını şimdi nasıl okur?

HotXLS okuyucusu artık t özniteliği bulunan ve değeri data olmayan her item'i atlar; çünkü ara toplam, genel toplam ve boş girdileri cache indeksi taşımaz. v2.384.34 öncesinde bu girdiler CacheItemIndex'i -1 yapılmış sıradan itemlar olarak yükleniyordu; Excel'in yaptığı bir pivot, hiçbir yere işaret etmeyen hayalet üyelerle dönüyordu ve Items üzerinde gezen her kod onları elle filtrelemek zorundaydı. Yazıcı sondaki girdileri Subtotals'dan yeniden kurduğuna göre okuyucunun işi onları o kümeye çevirmektir, veri olarak tutmak değil

İkinci okuyucu düzeltmesi, bulunmayan özniteliklerle ilgilidir. Şemada CT_PivotField üzerindeki defaultSubtotal ile CT_SharedItems üzerindeki containsString varsayılanı true'dur ve Excel o varsayılanı tuttuklarında bunları yazmaz. HotXLS eksik bir özniteliği false okuyordu; bu da Excel'in kaydettiği her pivotun yüklemede sessizce varsayılan ara toplamını kaybedeceği ve düz metin bir cache alanının dize yerine karma olarak sınıflanacağı anlamına geliyordu. Bu, axis hatasının ayna görüntüsüdür: her özniteliği her zaman açıkça yazan bir yazıcı varsayılan yolunu hiç çalıştırmaz; yolu yalnızca başka bir üreticinin dosyaları açığa çıkarır

numFmtId="General" cache alanlarında neden geçersizdi?

numFmtId="General" değeri geçersizdi, çünkü ST_NumFmtId bir biçim adı değil işaretsiz tamsayıdır. Eski cache yazıcısı bu dizeyi her cacheField'e, kullanıcının Hücreleri Biçimlendir penceresinde gördüğü adı ödünç alarak, sabit kodluyordu. HotXLS artık cache alanının NumberFormat'ını sayı olarak yazar; bir şey atamadıysa bu 0'dır (yerleşik General biçimi). Öznitelikleri şemadan tiplenen katı bir ayrıştırıcı eski değeri tümüyle reddeder ve tam da onarım penceresine dönüşen başarısızlık sınıfı budur; Excel onarım isteminin ardındaki OPC ve işaretleme kuralları makalesi bu pencerelerin nasıl tetiklendiğini anlatır

65535. satırın altındaki pivot tablolar neden kesiliyordu?

65536. satıra yerleşen ya da onun altındaki XLSX pivot tabloları kesiliyordu, çünkü paylaşılan pivot modeli FirstRow, LastRow, FirstHeaderRow, FirstDataRow ve sütun karşılıklarını Word olarak saklıyor, satır kaydırma kodu onları Min(.., High(Word)) ile kısılıyordu. Bu, 16 bitin yeterli olduğu BIFF8 SxView kaydından kalma bir şeydi; oysa bir XLSX sayfası 1.048.576 satıra kadar çıkar. v2.384.37'den beri TXLSPivotTable üzerindeki bu özellikler Integer'dır, kıyımlar kalkmıştır ve değerleri yalnızca BIFF8 yazıcısı daraltır. TXLSXWorksheet.AddPivotTable ve AddPivotTableCopy, 1..1048576 satır ve 1..16384 sütun dışında bir çapa için ya da uzantısı ızgarayı aşacak bir kopya için artık nil döndürür

HotXLS pivot çapası 70001. satırda, 16 bitlik tavana karşı: FirstRow ile LastRow Word olarak saklanıyor ve Min ile High(Word)'e, 65535'e kısalıyordu; v2.384.37 modeli Integer alanlara taşıyıp ızgara dışında nil döndürene dek çizgideki ve altındaki pivotlar kesiliyordu
Word alanları, sayfaları 1048576 satıra uzanan bir biçimde BIFF8 SxView kalıntısıydı — 65536. satırın ötesindeki bir çapa eskiden 16 bitlik aralığa sarılıyor ve pivotunu kaydetmede kaybediyordu
var
  Pivot: TXLSPivotTable;
  Check: TXLSXWorkbook;
begin
  // 70001. satır eskiden 16 bitlik aralığa sarılıyordu; artık kaydetme ve yüklemede hayatta kalır
  Pivot := Sheet.AddPivotTable('Data!$A$1:$D$800', 70001, 1, 'LateTotals');
  if Pivot = nil then
    Exit;  // çapa sayfa dışında ya da kaynak aralık çözümlenemiyor
  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;

Klasik XLS motoru eş düzeltmeyi v2.384.38'de aldı. Onun modeli ham 0 tabanlı SxView ve DConRef değerlerini saklıyor ve AddPivotTable çapalarını olduğu gibi geçiyordu; dokümantasyon, demolar ve XLSX motoru ise Cells[Row, Col] gibi 1 tabanlı hücreler kullanıyordu. Her iki motor da artık modelde 1 tabanlı konum tutar; BIFF8 okuyucusu 1 ekler, yazıcı kayıt sınırında 1 çıkarır; böylece (0, 0)'a çapa atan kodun (1, 1)'e taşınması gerekir, çünkü klasik AddPivotTable 1..65536 satır ve 1..256 sütun dışındaki bir çapa için artık nil döndürür; yeni çağrı, eskinin yazdığı baytların aynısını yazar. Kayıt yerleşiminin kendisi değişmedi ve klasik .xls pivot tablolarının ardındaki BIFF8 SX kayıtları makalesinde anlatılıyor

Kendi okuyucunuza değil şemaya göre doğrulayın

Ders pivotların ötesine geneller: hoşgörülü bir okuyucu yazıcı ihlallerini gizler; kendi kodunuzdan geçen bir gidiş dönüş tutarlılık kanıtlar, doğruluk değil. Buradaki her hata, toleranslı taraf ile hatalı tarafın aynı kütüphanede yaşaması sayesinde hayatta kaldı. Bu sınıf kusuru gerçekten yakalayan denetimler şunlardır: üretilen parçaların şema doğrulaması, Excel'in ürettiği ve öznitelikleri varsayılanlarında atlanmış dosyaların okuyucunuzdan geçirilmesi ve ayrıştırılmış sonuç yerine tam tokenı sabitleyen test verileri. API üzerinden kurduğunuz pivotlar — hesaplanan alanlarla XLSX pivot tabloları kurma ve yenileme makalesinde gösterilen hesaplanan alanlar, hesaplanan itemlar ve yüzde-of-toplam yerleşimleri dahil — kod değişikliği olmadan düzeltilmiş XML'i alır; Excel dosyalarından yüklenen pivotlar ise siz onları değiştirene dek özgün parçalarını yeniden oynatmaya devam eder

Bu düzeltmelerin tümü güncel HotXLS Delphi elektronik tablo bileşeninde gelir; bileşen XLS, XLSX ve pivot tablolarını makinede Excel ya da COM otomasyonu olmadan Delphi ve C++Builder'dan okur ve yazar