Artikel Teknis

Validitas Skema Pivot Field XLSX di Delphi dengan HotXLS

HotXLS menulis definisi pivot table XLSX yang elemen pivotField dan cacheField-nya valid terhadap skema ECMA-376 Part 1 §18.10: atribut axis memakai token ST_Axis axisRow, axisCol, dan axisPage, field di area nilai membawa dataField="1", daftar item tidak pernah kosong, dan cache field menyimpan numFmtId numerik. Sejak v2.384.33 reader juga menghormati default skema yang dulu salah ia perlakukan

Bug di balik pembersihan ini berbagi satu sifat yang tidak membanggakan: tidak satu pun pernah membuat tes gagal. HotXLS menulis sebuah pivot, HotXLS membacanya kembali, semua field mendarat di axis yang benar, dan suite round-trip tetap hijau bertahun-tahun. Masalahnya, writer dan reader diam-diam sepakat pada dialek privat. Pivot yang dibangun dari Delphi tampak wajar bagi komponen pembuatnya, sementara pemeriksaan terhadap CT_PivotField dan CT_CacheField menemukan token enumerasi invalid, satu elemen kosong yang dilarang skema, dan flag yang Excel harapkan tapi tak pernah dapat. Kalau Anda membuat pivot di server lalu mengirimkannya ke orang-orang yang membukanya di Excel atau menyandarkannya ke parser mereka sendiri, satu-satunya kontrak yang berarti adalah skema, bukan apa pun yang kebetulan dimaafkan reader Anda

Mengapa round trip HotXLS tidak pernah menangkap token axis yang salah?

Round trip HotXLS tidak pernah menangkap token axis yang salah karena reader menerima kedua ejaan. XlsxPivotAxisAttr lama meng-emit axis="rowAxis", colAxis, dan pageAxis, yang terbaca natural dalam bahasa Inggris tapi tidak ada di skema; ST_Axis mendefinisikan tepat empat nilai, axisRow, axisCol, axisPage, dan axisValues. Sementara itu PivotAxisFromToken di lxPivotXml.pas mencocokkan token skema dan token rekaan sekaligus, jadi semua self-test lolos. Writer kini hanya meng-emit token skema, dan reader tetap menerima ejaan lama supaya berkas yang disimpan versi HotXLS sebelumnya masih termuat dengan layoutnya utuh

<!-- sebelum v2.384.33: nilai ST_Axis invalid, CT_Items kosong -->
<pivotField axis="rowAxis" defaultSubtotal="1"><items count="0"></items></pivotField>

<!-- sejak 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 HotXLS sebelum dan sesudah v2.384.33 di mana nilai axis rekaan rowAxis dan elemen items kosong melanggar CT_PivotField sampai writer meng-emit token ST_Axis seperti axisRow dengan entry item nyata, flag hidden yang dipertahankan, dan subtotal default di ekor yang diterima skema
Reader yang longgar menerima kedua ejaan, jadi semua round trip lolos sementara berkasnya menggagalkan pemeriksaan skema ketat mana pun — tulis hanya empat token ST_Axis dan biarkan CT_Items membawa minimal satu item

Apa yang disyaratkan CT_PivotField tapi dilewatkan writer lama?

CT_PivotField menuntut tiga hal yang BuildPivotTableXml lama tinggalkan atau salah kerjakan. Pertama, field yang diagregasi di area nilai harus mengatakannya sendiri di definisinya dengan dataField="1"; writer kini memasang flag itu di setiap field yang dirujuk sebuah entry di DataFields, bukan hanya di list <dataFields>. Kedua, CT_Items butuh minimal satu item, jadi field tanpa item tidak lagi mendapat <items count="0"> kosong dan keseluruhan elemen itu saja yang dihilangkan. Ketiga, tiap item menjaga state-nya: h="1" untuk item tersembunyi (TXLSPivotItem.IsHidden) dan sd="0" untuk detail yang diciutkan (IsDetailHidden), keduanya dulu dibuang writer lama di setiap save

Bagian subtannya adalah item subtotal di ekor. Saat sebuah field punya item, Excel menampilkan satu item ekstra per fungsi subtotal setelah item-item data, bertipe dengan ST_ItemType: <item t="default"/> untuk subtotal otomatis, lalu sum, countA, avg, max, min, product, count, stdDev, stdDevP, var, dan varP untuk yang eksplisit. HotXLS menurunkan entry-entry itu dari TXLSPivotField.Subtotals saat save dan menghitungnya ke dalam items count. Field yang dibuat AddPivotTable mulai dengan set Subtotals kosong, yang menulis defaultSubtotal="0" dan tanpa item ekor, jadi mintalah subtotal secara eksplisit saat laporan membutuhkannya. Perhatikan jebakan penamaannya: xlpsCount dipetakan ke countA (semua entry) dan xlpsCountNums dipetakan ke count (hanya angka)

Anatomi list item pivot HotXLS di mana entry item data diikuti item subtotal di ekor yang diturunkan dari TXLSPivotField.Subtotals seperti t=default dan t=avg serta dihitung ke dalam items count, dengan jebakan penamaan xlpsCount ke countA dan xlpsCountNums ke count dijelaskan gamblang
Field dari AddPivotTable mulai dengan set Subtotals kosong, yang menulis defaultSubtotal=0 dan tanpa item ekor — mintalah fungsi yang Anda inginkan dan writer menurunkan satu item per fungsi ke dalam count
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];                  // berbasis 1, seperti engine 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 kalau tidak ada field itu
    if Region <> nil then
      Region.Subtotals := [xlpsDefault, xlpsAverage];  // -> t="default", t="avg"
    Pivot.AddColumnField('Quarter');
    Pivot.AddDataFieldByName('Revenue', xlpaSum);      // menandai Revenue dataField="1"

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

Bagaimana HotXLS kini membaca item subtotal dan default skema?

Reader HotXLS kini melewati setiap item yang atribut t-nya hadir dan bukan data, karena entry subtotal, grand total, dan blank tidak membawa indeks cache. Sebelum v2.384.34 entry-entry itu dimuat sebagai item biasa dengan CacheItemIndex diset -1, jadi pivot buatan Excel kembali dengan anggota hantu yang tidak menunjuk ke mana pun, dan kode mana pun yang menelusuri Items harus menyaringnya dengan tangan. Karena writer membangun ulang entry ekor dari Subtotals, tugas reader adalah menerjemahkannya ke set itu, bukan menyimpannya sebagai data

Fix reader kedua menyangkut atribut yang absen. Di skema, defaultSubtotal pada CT_PivotField dan containsString pada CT_SharedItems sama-sama default true, dan Excel menghilangkannya saat nilainya memang default itu. HotXLS dulu membaca atribut yang hilang sebagai false, yang berarti setiap pivot yang disimpan Excel diam-diam kehilangan subtotal default-nya saat load, dan cache field teks polos terklasifikasi mixed alih-alih string. Ini cermin dari bug axis: writer yang selalu mengeja setiap atribut tidak pernah menjalani jalur default, jadi hanya berkas dari produsen lain yang mengeksposnya

Mengapa numFmtId="General" invalid di cache field?

Nilai numFmtId="General" invalid karena ST_NumFmtId adalah integer unsigned, bukan nama format. Cache writer lama meng-hard-code string itu di setiap cacheField, meminjam nama yang dilihat pengguna di dialog Format Cells. HotXLS kini menulis NumberFormat milik cache field sebagai angka, yaitu 0 (format General bawaan) kecuali ada yang mengesetnya. Parser ketat yang bertipe atribut langsung dari skema menolak nilai lama mentah-mentah, dan persis itulah kelas kegagalan yang berubah jadi dialog perbaikan; artikel tentang aturan OPC dan markup di balik prompt perbaikan Excel membahas bagaimana dialog-dialog itu terpicu

Mengapa pivot table di baris 65535 ke bawah terpotong?

Pivot table XLSX yang diletakkan di baris 65536 atau di bawahnya terpotong karena model pivot bersama menyimpan FirstRow, LastRow, FirstHeaderRow, FirstDataRow, dan padanan kolomnya sebagai Word, dan kode penggeser baris menjepitnya dengan Min(.., High(Word)). Itu sisa record BIFF8 SxView, tempat 16 bit cukup, padahal sheet XLSX berjalan sampai 1,048,576 baris. Sejak v2.384.37 properti-properti itu di TXLSPivotTable menjadi Integer, jepitan dihapus, dan hanya writer BIFF8 yang menyempitkan nilainya. TXLSXWorksheet.AddPivotTable dan AddPivotTableCopy kini mengembalikan nil untuk anchor di luar 1..1048576 × 1..16384, atau untuk copy yang bentangannya akan keluar grid

Anchor pivot HotXLS di baris 70001 melawan plafon 16-bit di mana FirstRow dan LastRow tersimpan sebagai Word dan dijepit dengan Min terhadap High(Word) di 65535, memotong pivot di garis itu atau di bawahnya sampai v2.384.37 memindahkan model ke field Integer dengan return nil di luar grid
Field Word adalah sisa SxView BIFF8 di format yang sheet-nya berjalan sampai 1048576 baris — anchor melampaui baris 65536 dulu wrap ke rentang 16-bit dan kehilangan pivot-nya saat save
var
  Pivot: TXLSPivotTable;
  Check: TXLSXWorkbook;
begin
  // Baris 70001 dulu wrap ke rentang 16-bit; kini selamat dari save dan load
  Pivot := Sheet.AddPivotTable('Data!$A$1:$D$800', 70001, 1, 'LateTotals');
  if Pivot = nil then
    Exit;  // anchor di luar sheet atau rentang sumber yang tak teresolusi
  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;

Engine XLS klasik mendapat fix penyeimbangnya di v2.384.38. Modelnya dulu menyimpan nilai 0-based mentah SxView dan DConRef serta meneruskan anchor AddPivotTable apa adanya, sementara dokumentasi, demo, dan engine XLSX semuanya memakai sel berbasis 1 seperti Cells[Row, Col]. Kedua engine kini menyimpan posisi berbasis 1 di model, reader BIFF8 menambah 1 dan writer mengurangi 1 di batas record, jadi kode yang meng-anchor di (0, 0) harus pindah ke (1, 1), karena AddPivotTable klasik kini mengembalikan nil untuk anchor di luar 1..65536 × 1..256; panggilan baru menulis byte yang sama dengan yang lama. Layout recordnya sendiri tidak berubah dan dijelaskan di record SX BIFF8 di balik pivot table .xls klasik

Validasi terhadap skema, bukan reader Anda sendiri

Pelajarannya menggeneralisasi ke luar pivot: reader yang longgar menyembunyikan pelanggaran writer, jadi round trip lewat kode Anda sendiri membuktikan konsistensi, bukan kebenaran. Setiap bug di sini selamat karena sisi toleran dan sisi yang cacat tinggal di library yang sama. Pemeriksaan yang benar-benar menangkap kelas cacat ini adalah validasi skema atas part yang dibuat, berkas buatan Excel yang disandarkan ke reader Anda dengan atribut dihilangkan di default-nya, dan fixture yang menyematkan token persisnya alih-alih hasil parse-nya. Pivot yang Anda bangun lewat API, termasuk calculated field, calculated item, dan layout percent-of-total yang dipamerkan di membangun dan me-refresh pivot table XLSX dengan calculated field, mendapat XML yang sudah dikoreksi tanpa perubahan kode, sementara pivot yang dimuat dari berkas Excel terus memutar ulang part aslinya sampai Anda memodifikasinya

Semua fix ini ikut dalam HotXLS Delphi spreadsheet component terkini, yang membaca dan menulis XLS, XLSX, dan pivot table dari Delphi dan C++Builder tanpa Excel atau COM automation di mesin