Deepen PracticeOrdered learning track

XSLT 2.0/3.0 with Saxon

Learn Java XML In Action - Part 019

Advanced XSLT 2.0 and XSLT 3.0 usage in Java systems with Saxon, including s9api integration, compiled stylesheet lifecycle, packages, maps, arrays, try/catch, streaming concepts, security, testing, and production patterns.

17 min read3318 words
PrevNext
Lesson 1932 lesson track1927 Deepen Practice
#java#xml#xslt#saxon+4 more

Part 019 — XSLT 2.0/3.0 with Saxon

Goal: mampu memakai XSLT 2.0/3.0 secara production-grade di Java, bukan hanya menjalankan stylesheet. Setelah part ini, kita ingin bisa memilih kapan JDK XSLT 1.0 cukup, kapan perlu Saxon, bagaimana mengatur lifecycle processor, bagaimana menguji transformasi, dan bagaimana menghindari security/performance failure mode.

Part 018 sudah membahas JAXP TransformerFactory. Itu adalah interface penting di Java, tetapi baseline JDK umumnya berhenti pada XSLT 1.0 semantics. Banyak sistem enterprise modern yang masih XML-heavy membutuhkan fitur yang jauh lebih kuat: grouping, typing, sequence processing, function, package modularity, error handling, streaming-oriented design, dan XPath 3.1 data model. Di titik itulah Saxon sering menjadi pilihan praktis.

XSLT dengan Saxon harus dipahami sebagai transformation runtime, bukan sekadar library utility.

Mental model utama:

  • compile stylesheet once jika stylesheet stabil;
  • execute many times dengan context berbeda;
  • pisahkan static context dari dynamic context;
  • jangan biarkan stylesheet production melakukan I/O liar;
  • validasi input dan output ketika transformasi adalah contract boundary;
  • perlakukan transformasi sebagai fungsi deterministik yang bisa diaudit.

1. Kaufman Deconstruction: Skill yang Harus Dikuasai

Agar skill ini tidak menjadi “hafal syntax XSLT”, pecah menjadi sub-skill berikut.

Sub-skillOutput PraktisSelf-Correction Signal
Memilih XSLT versionBisa memutuskan XSLT 1.0 vs 2.0/3.0Tidak memakai XSLT 3.0 hanya karena lebih baru
Memahami XDMBisa berpikir dalam sequence, item, atomic value, node, map, arrayTidak menganggap semua output XPath sebagai string
Saxon s9api lifecycleBisa compile, cache, load, execute, serializeTidak membuat processor/compiler setiap request
ParameterizationBisa inject variable/context secara eksplisitTidak concatenate string ke stylesheet/query
Modular stylesheetBisa memecah transformasi menjadi function/mode/packageTidak membuat stylesheet 3000 baris tanpa boundary
Error handlingBisa menangkap recoverable/non-recoverable transform failureTidak menelan error menjadi output kosong
Secure resource accessBisa mengontrol document(), URI resolution, extension functionsTidak membiarkan stylesheet membaca filesystem/network
Testing transformasiBisa golden test, XPath assertion, schema validationTest tidak bergantung pada whitespace/prefix accidental
Performance engineeringBisa mengurangi compile overhead, memory pressure, repeated lookupTidak benchmark tanpa warm-up dan payload realistik

Latihan 20 jam untuk part ini tidak mengejar semua fitur Saxon. Fokusnya: mampu membuat transformation module yang aman, cepat, testable, dan bisa berubah versi.


2. Kenapa XSLT 2.0/3.0?

XSLT 1.0 cukup untuk transformasi sederhana:

  • rename element;
  • copy dengan perubahan kecil;
  • generate HTML sederhana;
  • mapping field langsung;
  • identity transform override.

Namun begitu transformasi mulai memiliki grouping, typed conversion, conditional routing, multi-output, modular function, robust error handling, atau large-document strategy, XSLT 1.0 sering memaksa workaround yang rapuh.

2.1 Capability Shift

AreaXSLT 1.0 StyleXSLT 2.0/3.0 Style
Data modelNode-set dan string conversion sering implisitXDM sequence dan typed value lebih eksplisit
GroupingMuenchian grouping dengan keyxsl:for-each-group
FunctionsExtension atau named template workaroundxsl:function
RegexTerbatas atau extensionmatches, replace, analyze-string
Error handlingBiasanya fail hard atau messagexsl:try / xsl:catch di XSLT 3.0
IterationRecursive templatexsl:iterate di XSLT 3.0
Modularityinclude / importPackage model di XSLT 3.0
Data structuresXML temporary treesMaps/arrays via XPath 3.1 in XSLT 3.0 processors
Streaming conceptImplementation-dependentXSLT 3.0 mendefinisikan streaming feature model

2.2 Contoh: Grouping Order Lines

Masalah: partner mengirim order line flat, kita perlu group by SKU.

XSLT 1.0 bisa, tapi pattern-nya sulit dibaca. XSLT 2.0 membuat intent eksplisit.

<xsl:for-each-group select="/order/line" group-by="sku">
  <item sku="{current-grouping-key()}">
    <quantity>
      <xsl:value-of select="sum(current-group()/quantity/xs:decimal(.))"/>
    </quantity>
  </item>
</xsl:for-each-group>

Kelebihan production-nya bukan hanya lebih pendek. Kelebihannya adalah:

  • grouping key terlihat jelas;
  • aggregation terlihat jelas;
  • lebih mudah diuji;
  • lebih mudah direview oleh engineer lain;
  • lebih mudah dikaitkan ke business mapping rule.

3. Saxon sebagai Advanced XML Runtime di Java

Saxon biasanya dipakai ketika Java application membutuhkan implementasi XPath/XQuery/XSLT modern.

Ada dua pendekatan integrasi umum:

  1. JAXP compatibility mode — masih memakai TransformerFactory, tetapi provider-nya Saxon.
  2. Saxon s9api — memakai API native Saxon yang lebih ekspresif untuk XPath/XQuery/XSLT modern.

Untuk production-grade usage, s9api sering lebih nyaman karena memberi objek yang lebih sesuai dengan mental model Saxon: Processor, compiler, executable, transformer, serializer, XDM value.

3.1 Object Lifecycle

ObjectRoleProduction Rule
ProcessorGlobal Saxon configuration holderBuat sedikit, biasanya singleton per configuration
XsltCompilerCompile stylesheet dengan static contextJangan compile per request jika stylesheet stabil
XsltExecutableCompiled stylesheetCache berdasarkan stylesheet identity/version
Xslt30TransformerLoaded executable dengan dynamic contextBuat per execution atau pastikan lifecycle aman
SerializerOutput serialization targetBuat per output/request
XdmNode / XdmValueXDM input/valueJangan diperlakukan seperti DOM mutable tree

Rule penting: compiled artifact boleh panjang umur; execution context harus pendek umur.


4. Minimal s9api Transformation Skeleton

Contoh berikut adalah skeleton, bukan copy-paste final. Tujuannya menunjukkan boundary yang benar.

import net.sf.saxon.s9api.*;

import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;

public final class SaxonTransformService {
    private final Processor processor;
    private final XsltExecutable executable;

    public SaxonTransformService(String stylesheetXml) throws SaxonApiException {
        this.processor = new Processor(false); // false = do not request licensed features
        XsltCompiler compiler = processor.newXsltCompiler();

        // Static context configuration should be explicit.
        // Examples: schema awareness, URI resolver/resource policy, base URI, error listener.

        this.executable = compiler.compile(
            new StreamSource(new StringReader(stylesheetXml))
        );
    }

    public String transform(String inputXml, String correlationId) throws SaxonApiException {
        Xslt30Transformer transformer = executable.load30();

        transformer.setStylesheetParameters(java.util.Map.of(
            new QName("correlationId"), new XdmAtomicValue(correlationId)
        ));

        Serializer serializer = processor.newSerializer();
        StringWriter output = new StringWriter();
        serializer.setOutputWriter(output);
        serializer.setOutputProperty(Serializer.Property.METHOD, "xml");
        serializer.setOutputProperty(Serializer.Property.INDENT, "no");

        transformer.transform(
            new StreamSource(new StringReader(inputXml)),
            serializer
        );

        return output.toString();
    }
}

Production notes:

  • jangan menaruh XML sebagai String jika payload besar;
  • gunakan stream/file/channel sesuai workload;
  • compile stylesheet saat deployment startup atau saat registry refresh;
  • lakukan health check dengan sample transformasi;
  • jangan membangun stylesheet dari input user;
  • jangan expose Saxon exception mentah ke external caller.

5. Static Context vs Dynamic Context

Banyak bug XSLT production muncul karena static dan dynamic context dicampur.

5.1 Static Context

Static context adalah hal yang diketahui saat compile stylesheet:

  • stylesheet source;
  • imported/included modules;
  • namespace declarations;
  • static variables/parameters;
  • schema awareness jika dipakai;
  • base URI untuk resolving relative reference;
  • processor edition/configuration;
  • enabled/disabled extension function;
  • package dependency.

Perubahan static context biasanya berarti compiled stylesheet harus dibuat ulang.

5.2 Dynamic Context

Dynamic context adalah hal yang diberikan saat execution:

  • input XML;
  • initial mode/template;
  • runtime parameters;
  • current date/time jika tidak dikunci;
  • message listener;
  • output destination;
  • request/correlation metadata.

Dynamic context tidak boleh mempengaruhi compiled artifact secara diam-diam.

5.3 Checklist

Sebelum deploy stylesheet baru, tanyakan:

  • apakah static dependency berubah?
  • apakah imported stylesheet berubah?
  • apakah output schema masih sama?
  • apakah parameter wajib berubah?
  • apakah package version berubah?
  • apakah resolver policy masih menutup network/file access yang tidak diizinkan?
  • apakah compiled cache key akan berubah?

6. XDM: Data Model yang Harus Dipahami

Saxon modern berpikir dalam XDM, bukan DOM.

XDM item dapat berupa:

  • node;
  • atomic value;
  • function item;
  • map;
  • array;
  • sequence dari item.

Konsekuensi:

  • select="item" tidak selalu satu node;
  • value-of melakukan atomization dan string joining;
  • function bisa menerima sequence;
  • absence bukan sama dengan empty string;
  • () adalah empty sequence, bukan null;
  • map/array berguna untuk lookup/mapping, tapi harus dijaga agar tidak menjadi “mini application” di stylesheet.

6.1 Empty Sequence vs Empty String vs Missing Node

<customer>
  <name></name>
</customer>

XPath examples:

ExpressionMeaning
customer/namenode exists
customer/emailempty sequence jika tidak ada
string(customer/name)empty string
exists(customer/name)true
exists(customer/email)false
normalize-space(customer/name)empty string

Mapping rule harus membedakan:

  • field tidak dikirim;
  • field dikirim kosong;
  • field dikirim xsi:nil="true";
  • field dikirim whitespace;
  • field dikirim default value hasil schema.

7. XSLT 2.0 Features yang Paling Bernilai

7.1 xsl:for-each-group

Gunakan untuk grouping, aggregation, rollup, dan duplicate detection.

<xsl:for-each-group select="/invoice/line" group-by="normalize-space(productCode)">
  <product code="{current-grouping-key()}">
    <lineCount><xsl:value-of select="count(current-group())"/></lineCount>
    <total><xsl:value-of select="sum(current-group()/amount/xs:decimal(.))"/></total>
  </product>
</xsl:for-each-group>

Production pattern:

  • group key harus normalized;
  • type conversion harus eksplisit;
  • missing key harus diarahkan ke error/exception flow;
  • output aggregate harus divalidasi.

7.2 xsl:function

Gunakan untuk logic murni yang reuseable.

<xsl:function name="m:normalize-country" as="xs:string">
  <xsl:param name="raw" as="xs:string?"/>
  <xsl:sequence select="upper-case(normalize-space($raw))"/>
</xsl:function>

Batasan:

  • function harus pure;
  • jangan melakukan I/O;
  • jangan menyembunyikan business rule besar di function kecil tanpa test;
  • tipe parameter dan return wajib eksplisit untuk stylesheet penting.

7.3 Regex Processing

<xsl:analyze-string select="$customerRef" regex="^([A-Z]{3})-(\d{8})$">
  <xsl:matching-substring>
    <prefix><xsl:value-of select="regex-group(1)"/></prefix>
    <number><xsl:value-of select="regex-group(2)"/></number>
  </xsl:matching-substring>
  <xsl:non-matching-substring>
    <invalidCustomerRef><xsl:value-of select="$customerRef"/></invalidCustomerRef>
  </xsl:non-matching-substring>
</xsl:analyze-string>

Regex di stylesheet harus:

  • memiliki test case positif dan negatif;
  • tidak terlalu permisif;
  • tidak menjadi substitute untuk parser domain yang lebih cocok;
  • tidak menimbulkan catastrophic backtracking pada input besar.

8. XSLT 3.0 Features yang Paling Bernilai

8.1 xsl:try / xsl:catch

Useful untuk local error handling, terutama ketika transformasi punya fallback yang benar.

<xsl:try>
  <amount>
    <xsl:value-of select="format-number(xs:decimal($rawAmount), '0.00')"/>
  </amount>
  <xsl:catch>
    <mappingError code="INVALID_AMOUNT">
      <raw><xsl:value-of select="$rawAmount"/></raw>
    </mappingError>
  </xsl:catch>
</xsl:try>

Gunakan dengan hati-hati:

  • jangan catch semua error lalu output tampak sukses;
  • error semantic besar sebaiknya gagal transformasi;
  • fallback harus jelas dan diuji;
  • audit trail harus mencatat catch path.

8.2 xsl:iterate

Berguna untuk iterative processing dengan state eksplisit.

<xsl:iterate select="/events/event">
  <xsl:param name="runningTotal" select="0" as="xs:decimal"/>

  <xsl:on-completion>
    <total><xsl:value-of select="$runningTotal"/></total>
  </xsl:on-completion>

  <xsl:next-iteration>
    <xsl:with-param name="runningTotal" select="$runningTotal + xs:decimal(amount)"/>
  </xsl:next-iteration>
</xsl:iterate>

Ini lebih maintainable dibanding recursive template ketika state-nya memang linear.

8.3 Maps untuk Lookup

<xsl:variable name="countryMap" as="map(xs:string, xs:string)" select="map {
  'ID': 'Indonesia',
  'SG': 'Singapore',
  'MY': 'Malaysia'
}"/>

<countryName>
  <xsl:value-of select="map:get($countryMap, upper-case(countryCode))"/>
</countryName>

Gunakan map untuk lookup kecil/stabil. Untuk lookup besar atau berubah cepat:

  • inject sebagai external document;
  • precompile sebagai XDM map di Java;
  • kelola sebagai versioned reference data;
  • hindari call database langsung dari stylesheet.

8.4 Packages

XSLT 3.0 package membantu modularisasi transformation library.

Pattern:

transform-core/
  normalize.xsl
  date-functions.xsl
  money-functions.xsl

partner-a/
  inbound-order-v3.xsl
  outbound-status-v2.xsl

partner-b/
  inbound-order-v1.xsl

Package/module boundary yang sehat:

  • core function tidak tahu partner;
  • partner adapter tahu source/target contract;
  • output schema validation tetap di luar stylesheet atau di final stage;
  • package version dicatat di audit metadata;
  • breaking change harus memicu major version.

8.5 Streaming Concepts

XSLT 3.0 punya model streaming, tetapi production engineer harus hati-hati:

  • tidak semua stylesheet streamable;
  • tidak semua processor edition mendukung fitur streaming penuh;
  • operasi seperti arbitrary ancestor/descendant navigation bisa memecahkan streamability;
  • streaming mengubah cara berpikir: satu pass, no random access, limited lookahead.

Gunakan streaming ketika:

  • payload sangat besar;
  • transformasi mostly linear;
  • latency/memory lebih penting daripada complex random access;
  • output bisa dihasilkan incremental.

Jangan gunakan streaming ketika:

  • transformasi perlu global grouping kompleks;
  • perlu sort seluruh dokumen;
  • perlu cross-reference bebas;
  • payload cukup kecil dan DOM/XDM tree lebih sederhana.

9. Java Architecture: Versioned Transformation Engine

Di production, jangan biarkan controller/service langsung memanggil Saxon. Buat layer eksplisit.

9.1 Interface Design

public interface XmlTransformationEngine {
    TransformationResult transform(TransformationRequest request)
        throws TransformationException;
}

public record TransformationRequest(
    String transformationId,
    String transformationVersion,
    javax.xml.transform.Source input,
    java.util.Map<String, Object> parameters,
    String correlationId
) {}

public record TransformationResult(
    byte[] output,
    String outputMediaType,
    TransformationEvidence evidence
) {}

public record TransformationEvidence(
    String transformationId,
    String transformationVersion,
    String stylesheetChecksum,
    String inputSchemaVersion,
    String outputSchemaVersion,
    java.time.Instant startedAt,
    java.time.Instant completedAt,
    java.util.List<String> warnings
) {}

9.2 Cache Key

Compiled stylesheet cache key minimal:

transformationId
+ transformationVersion
+ stylesheetChecksum
+ processorConfigurationVersion
+ staticParameterFingerprint
+ dependencyBundleChecksum

Jangan hanya memakai filename. Filename tidak cukup untuk audit dan cache invalidation.

9.3 Hot Reload Strategy

Untuk enterprise integration, stylesheet sering berubah lebih cepat daripada application code.

Pilihan:

StrategyBenefitRisk
Bundle with applicationdeterministic deploymentlambat untuk partner fix
External registryupdate cepatgovernance/security lebih sulit
Hybridstable core + external adaptercomplexity sedang

Rule praktis:

  • stylesheet production harus immutable setelah published;
  • update membuat version baru;
  • rollback harus bisa menunjuk ke previous compiled version;
  • setiap version punya test fixture dan checksum;
  • jangan overwrite stylesheet lama secara diam-diam.

10. Resource Resolution and Security

XSLT bisa membaca resource external melalui mekanisme seperti import/include, document(), unparsed-text(), collection access, atau extension function. Ini powerful, tapi berbahaya.

10.1 Threat Model

ThreatExampleControl
SSRFStylesheet membaca http://169.254...Block network URI
File disclosurefile:///etc/passwd via documentBlock file URI kecuali allowlist
Supply-chain substitutionimported stylesheet berubahchecksum + immutable bundle
Extension abuseJava extension function melakukan I/Odisable atau allowlist ketat
Data exfiltrationoutput menyertakan secret dari resolverredaction + resolver policy
DoSstylesheet mahal atau input hugetimeout, size limit, execution budget

10.2 Resolver Policy

Design resolver sebagai whitelist, bukan blacklist.

public interface TransformationResourceResolver {
    javax.xml.transform.Source resolve(String href, String baseUri)
        throws TransformationResourceDeniedException;
}

Policy contoh:

ALLOW classpath:/xslt/common/*.xsl
ALLOW registry:/transformations/{id}/{version}/dependencies/*
DENY  file:*
DENY  http:*
DENY  https:* unless explicitly configured for internal immutable repository
DENY  relative path escaping bundle root

10.3 Secure Defaults

  • stylesheet source harus trusted;
  • input XML bisa untrusted;
  • external resource access default deny;
  • network access default deny;
  • extension function default deny;
  • output escaping tidak boleh dimatikan tanpa review;
  • disable-output-escaping biasanya anti-pattern;
  • logging tidak boleh mencetak payload sensitif penuh;
  • error message tidak boleh membocorkan path internal.

11. Error Handling Model

Transformation error bukan satu kategori.

Error TypeExampleRecommended Handling
Static compile errorinvalid stylesheet syntaxfail deployment/startup
Missing dependencyimport not foundfail version publication
Input parse errormalformed XMLreject input
Input contract errorinvalid XSDreject/quarantine input
Mapping errorinvalid amount/date/codefail transform or emit structured rejection
Output contract errorinvalid output XSDfail transform and alert
Processor internal errorSaxon exception unexpectedfail safely, alert
Resource deniedstylesheet tries forbidden URIfail and security log

11.1 Structured Exception

public final class TransformationException extends RuntimeException {
    private final String transformationId;
    private final String transformationVersion;
    private final String errorCode;
    private final boolean retryable;

    public TransformationException(
        String transformationId,
        String transformationVersion,
        String errorCode,
        boolean retryable,
        String message,
        Throwable cause
    ) {
        super(message, cause);
        this.transformationId = transformationId;
        this.transformationVersion = transformationVersion;
        this.errorCode = errorCode;
        this.retryable = retryable;
    }
}

Retryability rule:

  • bad input: usually not retryable;
  • missing reference data due to transient registry outage: maybe retryable;
  • stylesheet compile error: not retryable until deployment fixed;
  • output schema mismatch: not retryable without transformation fix.

12. Performance Engineering

12.1 Main Cost Centers

CostCauseControl
Compile costcompiling stylesheet per requestcache XsltExecutable
Parse costrepeated XML parsepass stream or XDM carefully
Memory costbuilding full treestreaming strategy or StAX pre-filter
Lookup costrepeated document/reference accesssnapshot reference data
Serialization costpretty print, encoding conversiondeterministic output settings
XPath costexpensive global selectionkeys, grouping, mode design

12.2 Avoid Compile Per Request

Bad:

public String transform(String xml) {
    Processor p = new Processor(false);
    XsltCompiler c = p.newXsltCompiler();
    XsltExecutable e = c.compile(stylesheetSource());
    return execute(e, xml);
}

Better:

public final class CompiledTransformation {
    private final XsltExecutable executable;

    public CompiledTransformation(XsltExecutable executable) {
        this.executable = executable;
    }

    public void execute(Source input, Destination output) throws SaxonApiException {
        Xslt30Transformer transformer = executable.load30();
        transformer.transform(input, output);
    }
}

12.3 Benchmarking Rules

  • benchmark dengan payload realistik;
  • pisahkan compile time dari execution time;
  • ukur p50/p95/p99 latency;
  • ukur allocation dan GC;
  • jalankan warm-up;
  • benchmark output validation secara terpisah dan gabungan;
  • catat processor version, JVM version, stylesheet checksum;
  • jangan hanya benchmark happy path.

12.4 Large Payload Strategy

Decision matrix:

Payload ShapeRecommended Strategy
small config XMLXDM/DOM OK
medium transactional payloadSaxon normal transform OK
huge flat documentstreaming/StAX prefilter/XSLT streaming if supported
huge document needing global sortconsider staged external sort or database
many independent records in one filesplit + transform records + aggregate evidence

13. Testing XSLT 2.0/3.0

Testing harus menangkap semantic correctness, bukan hanya exact string.

13.1 Test Pyramid

13.2 Golden Test Rules

Golden output berguna, tapi raw string comparison rapuh karena:

  • attribute order;
  • prefix choice;
  • whitespace;
  • XML declaration;
  • line ending;
  • serializer version.

Gunakan golden test dengan canonicalization atau XML-aware comparison.

13.3 XPath Assertion Example

assertThat(xml).hasXPath("/Order/customer/id", "C-123");
assertThat(xml).hasXPath("count(/Order/items/item)", "3");
assertThat(xml).hasXPath("sum(/Order/items/item/quantity)", "10");

13.4 Mapping Table Test

Untuk mapping partner, buat fixture berbasis rule.

Rule IDSource XPathTarget XPathCase
ORD-001/order/id/Order/orderIdpresent
ORD-002/order/customer/email/Order/customer/emailmissing
ORD-003/order/line/Order/items/itemmultiple
ORD-004/order/date/Order/orderDatetimezone edge

Setiap rule harus punya minimal:

  • positive case;
  • missing field case;
  • invalid value case;
  • boundary value case;
  • backward compatibility case jika schema berubah.

13.5 Security Tests

Test resolver denial:

  • stylesheet mencoba document('file:///etc/passwd');
  • stylesheet mencoba document('http://example.com/a.xml');
  • stylesheet mencoba relative path traversal;
  • stylesheet import dependency tidak terdaftar;
  • input mengandung DTD/entity;
  • transformation output mencoba menghasilkan field sensitif tidak diizinkan.

14. Observability and Audit

Transformation service harus menjawab:

  • stylesheet version apa yang dipakai?
  • input schema version apa?
  • output schema version apa?
  • parameter apa yang diberikan?
  • reference data version apa?
  • error terjadi pada phase mana?
  • output valid atau tidak?
  • transformasi deterministic atau memakai current date/random value?

14.1 Metrics

Minimal metrics:

xml_transform_requests_total{transformation_id,version,result}
xml_transform_duration_seconds{transformation_id,version}
xml_transform_input_bytes{transformation_id,version}
xml_transform_output_bytes{transformation_id,version}
xml_transform_compile_duration_seconds{transformation_id,version}
xml_transform_cache_hit_total{transformation_id,version}
xml_transform_validation_failures_total{schema_version,phase}
xml_transform_resource_denied_total{resource_scheme,transformation_id}

14.2 Log Events

{
  "event": "xml_transformation_completed",
  "correlationId": "abc-123",
  "transformationId": "partner-a-order-inbound",
  "transformationVersion": "3.2.1",
  "stylesheetChecksum": "sha256:...",
  "inputSchemaVersion": "partner-a-order-v3",
  "outputSchemaVersion": "canonical-order-v5",
  "durationMs": 42,
  "inputBytes": 18342,
  "outputBytes": 9210,
  "warningCount": 0
}

Do not log full XML payload by default. Log payload references, hashes, and redacted snippets.


15. XSLT Review Checklist

Gunakan checklist ini sebelum stylesheet dipromosikan ke production.

15.1 Correctness

  • Input contract jelas.
  • Output contract jelas.
  • Missing/blank/nil semantics ditentukan.
  • Date/time conversion eksplisit.
  • Decimal/currency precision eksplisit.
  • Namespace target benar.
  • Output divalidasi.
  • Mapping rule punya traceability.

15.2 Maintainability

  • Stylesheet tidak terlalu panjang tanpa module.
  • Function reusable diberi type signature.
  • Mode dipakai untuk phase yang berbeda.
  • Parameter terdokumentasi.
  • Tidak ada magic string tanpa registry.
  • Tidak ada commented-out production logic.

15.3 Security

  • External resource access default deny.
  • Extension function disabled atau allowlisted.
  • Stylesheet source trusted.
  • Input parser secure.
  • Output escaping tidak dimatikan.
  • Error tidak leak path/secret.

15.4 Performance

  • Stylesheet compiled/cached.
  • Payload size sudah diuji.
  • Global XPath mahal direview.
  • Reference data tidak dibaca per node dari source eksternal.
  • p95/p99 latency diketahui.

15.5 Operations

  • Version immutable.
  • Rollback path ada.
  • Audit metadata lengkap.
  • Metrics ada.
  • Fixture regression lengkap.
  • Owner schema/stylesheet jelas.

16. Common Anti-Patterns

16.1 Stylesheet as Application Server

Gejala:

  • stylesheet call database;
  • stylesheet call HTTP service;
  • stylesheet membaca banyak file dynamic;
  • business workflow ditulis dalam XSLT;
  • retry logic ada di stylesheet.

Masalah:

  • sulit diobservasi;
  • sulit di-scale;
  • sulit di-secure;
  • sulit di-test;
  • error semantics tidak jelas.

Solusi:

  • lakukan enrichment di Java pipeline sebelum XSLT;
  • inject reference snapshot sebagai parameter/document yang versioned;
  • biarkan XSLT fokus ke deterministic transformation.

16.2 Catch-and-Continue Everything

Gejala:

<xsl:try>
  <!-- all mapping -->
  <xsl:catch>
    <xsl:message>ignored</xsl:message>
  </xsl:catch>
</xsl:try>

Ini berbahaya karena output bisa tampak valid tetapi salah secara bisnis.

16.3 Prefix-Coupled Stylesheet

Salah:

<xsl:value-of select="/p:Order/p:Customer/p:Name"/>

Ini benar jika namespace binding tepat. Yang salah adalah berpikir prefix literal di XML input harus sama. XPath harus bind namespace URI, bukan tergantung prefix input.

16.4 Non-Deterministic Transform

Gejala:

  • current date dipakai tanpa parameter;
  • random ID dibuat di stylesheet;
  • reference data dibaca dari lokasi mutable;
  • output berbeda untuk input sama tanpa evidence.

Untuk regulatory/audit systems, ini fatal. Determinism harus dijaga.


17. Practice Drill

Buat transformation module berikut.

Input

Partner order XML:

<po:purchaseOrder xmlns:po="urn:partner:a:po:v3">
  <po:id>PO-1001</po:id>
  <po:customer>
    <po:id>C-77</po:id>
    <po:country>id</po:country>
  </po:customer>
  <po:lines>
    <po:line>
      <po:sku>A-1</po:sku>
      <po:qty>2</po:qty>
      <po:price>100.00</po:price>
    </po:line>
    <po:line>
      <po:sku>A-1</po:sku>
      <po:qty>3</po:qty>
      <po:price>100.00</po:price>
    </po:line>
  </po:lines>
</po:purchaseOrder>

Output

Canonical order XML:

<Order xmlns="urn:company:canonical:order:v5">
  <orderId>PO-1001</orderId>
  <customerId>C-77</customerId>
  <countryCode>ID</countryCode>
  <items>
    <item sku="A-1">
      <quantity>5</quantity>
      <totalAmount>500.00</totalAmount>
    </item>
  </items>
</Order>

Requirements

  • group lines by SKU;
  • normalize country to uppercase;
  • reject missing SKU;
  • amount must be decimal with two fraction digits;
  • stylesheet must accept correlationId parameter;
  • output must be schema-valid;
  • compiled stylesheet must be cached;
  • test must include duplicate SKU, missing SKU, invalid decimal, unknown country.

Self-correction:

  • jika output valid tapi quantity salah, grouping logic salah;
  • jika test tergantung prefix, namespace test salah;
  • jika missing SKU menjadi empty attribute, error handling salah;
  • jika repeated execution compile ulang, lifecycle salah;
  • jika invalid decimal menjadi NaN/empty output, type conversion/error handling salah.

18. Summary

XSLT 2.0/3.0 dengan Saxon memberi Java system kemampuan transformation yang jauh lebih kuat daripada XSLT 1.0 default. Tetapi kemampuan ini harus dibatasi dengan architecture yang jelas.

Prinsip inti:

  1. treat stylesheet as versioned executable contract;
  2. compile once, execute many times;
  3. separate static context from dynamic context;
  4. control all resource access;
  5. validate input and output where transformation crosses contract boundary;
  6. test semantic result with XPath/schema/golden fixtures;
  7. capture audit evidence;
  8. avoid turning XSLT into application server.

Part berikutnya akan membahas transformation patterns, canonicalization, dan mapping strategy agar kita bisa mendesain transformasi sebagai architecture layer, bukan sekadar file .xsl yang menumpuk.


References

  • W3C, XSL Transformations (XSLT) Version 3.0.
  • W3C, XPath and XQuery Functions and Operators 3.1.
  • W3C, XPath 3.1.
  • Saxonica, Saxon s9api documentation.
  • Saxonica, Using s9api for XSLT transformations.
  • Oracle, Java XML Processing APIs.
Lesson Recap

You just completed lesson 19 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.