Skip to content

[feature]: Add configurable XSD/XSLT profiles for application-specific XML rendering #305

Description

@p4535992

Area

Framework component or public API

User problem and use case

Applications embedding File Viewer may need to preview domain-specific XML documents whose structure and presentation rules are only known by the host application.

Today File Viewer can display XML as source, but it cannot know whether an application-specific XML document conforms to a particular schema or whether that format provides an XSLT stylesheet intended to produce a human-readable representation.

A common case is an XML format distributed with:

  • an XSD schema describing and validating the document structure;
  • an XSLT stylesheet defining a readable HTML representation.

The desired workflow is to let the host application register these resources once and then open XML files normally through File Viewer.

The host application should not need to detect, validate and transform every XML document before passing it to File Viewer.

A typical self-hosted deployment could provide a directory such as:

/file-viewer/xml-profiles/
├── profiles.json
├── invoice/
│   ├── schema.xsd
│   └── style.xsl
└── order/
    ├── schema.xsd
    └── style.xsl

The application would only need to configure the profile manifest location, for example:

const options = {
  xml: {
    profilesUrl: '/file-viewer/xml-profiles/profiles.json'
  }
}

The manifest could associate each XML format with its XSD and XSLT resources:

{
  "profiles": [
    {
      "id": "invoice-v1",
      "match": {
        "rootNamespace": {
          "enabled": true,
          "root": "invoice",
          "namespace": "urn:example:invoice:v1"
        },
        "xsd": {
          "enabled": true
        }
      },
      "xsd": "./invoice/schema.xsd",
      "xslt": "./invoice/style.xsl"
    }
  ]
}

Relative XSD/XSLT paths would be resolved relative to the profile manifest so all resources can remain self-hosted with the application.

Proposed outcome

Please consider adding an optional XML rendering-profile capability.

Each profile may enable two independent checks:

  1. root element + namespace matching;
  2. XSD validation.

Either check can be enabled individually.

When multiple checks are enabled, every enabled check must succeed before the profile is accepted and its XSLT is applied.

Root element + namespace check

When rootNamespace.enabled is true, File Viewer checks the XML document root local name and namespace.

For example:

<invoice xmlns="urn:example:invoice:v1">

can be matched against:

{
  "rootNamespace": {
    "enabled": true,
    "root": "invoice",
    "namespace": "urn:example:invoice:v1"
  }
}

This is a lightweight check and can quickly reject unrelated profiles without running full schema validation.

XSD check

When xsd.enabled is true, File Viewer validates the XML document against the XSD associated with the profile.

This is a stronger check because it validates the actual document structure, required elements, attributes and datatypes rather than only its namespace and root element.

For example:

{
  "match": {
    "rootNamespace": {
      "enabled": false
    },
    "xsd": {
      "enabled": true
    }
  },
  "xsd": "./invoice/schema.xsd",
  "xslt": "./invoice/style.xsl"
}

allows profile matching based on XSD validation only.

Using both checks

The recommended configuration would enable both checks:

{
  "match": {
    "rootNamespace": {
      "enabled": true,
      "root": "invoice",
      "namespace": "urn:example:invoice:v1"
    },
    "xsd": {
      "enabled": true
    }
  }
}

The intended flow is:

XML
 │
 ▼
root + namespace check
 │
 ├── fail ──────────────────► next candidate / XML source fallback
 │
 ▼
candidate profile
 │
 ▼
XSD validation
 │
 ├── fail ──────────────────► XML source fallback + diagnostics
 │
 ▼
confirmed profile
 │
 ▼
XSLT transformation
 │
 ├── fail ──────────────────► XML source fallback + diagnostics
 │
 ▼
safe rendered output

This gives the inexpensive namespace/root check the role of candidate selection while XSD validation provides stronger confirmation.

There is no need for an additional all / any matching operator: all enabled checks simply have to succeed.

Root/namespace only

Applications that trust a stable and unique XML namespace and do not need schema validation could disable the XSD check:

{
  "match": {
    "rootNamespace": {
      "enabled": true,
      "root": "invoice",
      "namespace": "urn:example:invoice:v1"
    },
    "xsd": {
      "enabled": false
    }
  }
}

XSD only

Applications that cannot reliably identify a document from its namespace/root could disable the lightweight check:

{
  "match": {
    "rootNamespace": {
      "enabled": false
    },
    "xsd": {
      "enabled": true
    }
  }
}

In this mode File Viewer may need to validate the document against multiple registered XSD profiles.

The result should be deterministic:

0 valid XSD profiles
    -> normal XML source preview

1 valid XSD profile
    -> select profile and apply its XSLT

more than 1 valid XSD profile
    -> ambiguous profile
    -> do not select arbitrarily
    -> XML source preview + diagnostic

Complete processing flow

When an XML document is opened, File Viewer would:

  1. Parse the XML safely.
  2. Find registered profiles whose enabled lightweight checks match.
  3. Run XSD validation for profiles where the XSD check is enabled.
  4. Reject any profile for which an enabled check fails.
  5. Select the profile only when the result is unambiguous.
  6. Apply the associated XSLT stylesheet.
  7. Render the transformation result through an isolated/safe File Viewer rendering surface.
  8. Fall back to the normal XML source preview if no profile matches, validation fails, profile selection is ambiguous, or XSLT transformation fails.

Acceptance criteria

  • XML profile support is opt-in and does not alter existing XML rendering by default.
  • A host application can register profiles through one local/self-hosted manifest URL or equivalent public API.
  • XSD and XSLT paths may be relative to the manifest location.
  • Root/namespace matching and XSD validation are independently configurable checks.
  • Either check may be enabled individually.
  • Both checks may be enabled together.
  • When multiple checks are enabled, every enabled check must succeed.
  • A failed enabled check prevents the profile's XSLT from being applied.
  • Root/namespace matching can narrow candidate profiles before the more expensive XSD validation.
  • XSD-only matching is supported when namespace/root information is insufficient.
  • If XSD-only matching validates against exactly one profile, that profile may be selected.
  • If multiple profiles validate successfully, File Viewer must not select one arbitrarily.
  • A valid matching document is transformed using the associated XSLT stylesheet.
  • No matching profile falls back to the existing XML source renderer.
  • XSD validation failure falls back to XML source preview rather than preventing the file from being opened.
  • XSLT transformation failure also falls back to XML source preview.
  • Validation/transformation diagnostics may be surfaced where practical.
  • XSD/XSLT/WASM dependencies are lazy-loaded only when needed.
  • Required runtime assets can be self-hosted for offline/intranet deployments.
  • The original XML remains unchanged and remains the authoritative downloadable source.
  • HTML generated through XSLT is treated as untrusted document output.
  • Document-generated scripts must not execute in the host page.
  • External resources referenced by XSD/XSLT must not have unrestricted network access by default.

The exact property names and manifest schema above are only suggestions.

The important outcome is that an embedding application can register XML schema/style pairs once and File Viewer can automatically identify, validate and render matching XML documents.

Samples, specifications, or references

A small redistributable fixture can be attached to the issue as:

invoice-invalid.xml
invoice-valid.xml
profiles.json

invoice_is_a_xsd.xml
invoice_is_a_xsl.xml
invoice-invalid.xml
invoice-valid.xml
profiles.json

flyfish-xml-profile-sample-v2.zip

It contains:

profiles.json
invoice-valid.xml
invoice-invalid.xml
invoice.xsd
invoice.xsl
README.txt

The fixture was created specifically for this feature proposal and contains no customer or third-party data.

invoice-valid.xml is expected to behave as follows:

root/namespace check   PASS
XSD validation         PASS
XSLT transformation    EXECUTED
result                 rendered HTML

invoice-invalid.xml intentionally uses the correct root element and namespace but is missing a required element:

root/namespace check   PASS
XSD validation         FAIL
XSLT transformation    NOT EXECUTED
result                 XML source preview + optional diagnostic

This demonstrates why root/namespace matching and XSD validation are useful as separate checks.

Specifications

XSLT 1.0 — W3C Recommendation:

https://www.w3.org/TR/xslt-10/

XML Schema 1.0 Part 1 — Structures:

https://www.w3.org/TR/xmlschema-1/

XML Schema 1.0 Part 2 — Datatypes:

https://www.w3.org/TR/xmlschema-2/

XSLT implementation candidate

xslt-polyfill

Repository:

https://github.com/mfreed7/xslt_polyfill

License: BSD-3-Clause

The project provides an XSLTProcessor-compatible implementation backed by libxslt/libxml2 WebAssembly.

Public sample XML:

https://github.com/mfreed7/xslt_polyfill/blob/main/test/demo.xml

Public XSLTProcessor example:

https://github.com/mfreed7/xslt_polyfill/blob/main/test/XSLTProcessor_example.html

XSD implementation candidate

xmllint-wasm

Repository:

https://github.com/noppa/xmllint-wasm

License: MIT

The project provides libxml2-based XML/XSD validation through WebAssembly and documents browser-side XSD validation.

Alternatives considered

Existing XML source preview

Displaying XML source is useful and should remain the fallback behavior, but it cannot provide the format-specific presentation that the XML producer intended through its XSLT.

The host application currently has to detect the format and perform validation/transformation itself before handing the result to File Viewer.

This duplicates XML handling outside the viewer and means the viewer may receive generated HTML instead of the original document.

Namespace-only profile matching

Matching root element and namespace is inexpensive and useful for XML formats with stable, unique namespaces.

However, it only identifies the apparent document family.

For example, this XML could have the expected namespace:

<invoice xmlns="urn:example:invoice:v1">

while still missing required elements or containing invalid values.

For that reason root/namespace matching and XSD validation should be exposed as separate checks.

Applications can choose lightweight matching only, schema validation only, or enable both.

Trying every registered XSD unconditionally

File Viewer could validate every XML document against every registered schema.

This provides strong detection but may become expensive when applications register many schemas.

Enabling both checks provides a more efficient path:

root/namespace
      ↓
small candidate set
      ↓
XSD validation

XSD-only matching remains available where necessary.

Pre-transforming XML before passing it to File Viewer

The application could apply XSLT itself and pass generated HTML to the viewer.

This works, but requires each embedding application to independently implement:

  • XML format detection;
  • schema loading;
  • XSD validation;
  • XSLT processing;
  • error handling;
  • fallback behavior.

It also separates the displayed result from the original file that File Viewer is previewing.

Native browser XSLTProcessor

Historically browsers exposed XSLT 1.0 through XSLTProcessor.

Relying on the browser's native implementation is no longer a good long-term foundation as native XSLT support is being deprecated/removed from browser engines.

A self-hostable WASM implementation is better aligned with File Viewer's browser-side/offline model.

SaxonJS

SaxonJS supports significantly more advanced XSLT capabilities.

However, it introduces a different browser deployment model and commonly uses compiled SEF stylesheets.

For an initial implementation intended to consume existing .xsl resources directly, an XSLT 1.0/libxslt implementation is simpler.

xslt-processor

xslt-processor can perform JavaScript XSLT processing but is LGPL-3.0 licensed.

For an Apache-2.0 project, a permissively licensed implementation is preferable to keep browser bundling and redistribution simpler.

Server-side conversion

XSD validation and XSLT transformation could be delegated to a backend.

That would work technically but would remove an important benefit of File Viewer: private/offline browser-side preview without requiring a document-conversion service.

Dependency and license notes

XSLT: xslt-polyfill

Suggested use: XSLT 1.0 transformation.

License: BSD-3-Clause.

Repository:

https://github.com/mfreed7/xslt_polyfill

The implementation provides an XSLTProcessor-compatible API backed by WebAssembly ports of libxslt and libxml2.

This appears compatible with File Viewer's browser/WASM and self-hosted asset model.

The proposed integration should use its transformation API rather than automatic whole-document replacement behavior.

The generated output should pass through File Viewer's normal isolation/sanitization strategy.

XSLT mechanisms such as:

  • xsl:include;
  • xsl:import;
  • document();

need explicit resource-resolution rules.

A conservative implementation could initially allow only registered/self-hosted resources.

The XSLT runtime should be loaded lazily only when a selected XML profile actually requires transformation.

XSD: xmllint-wasm

Suggested use: XML Schema validation and, when requested, XSD-based profile matching.

License: MIT.

Repository:

https://github.com/noppa/xmllint-wasm

It exposes libxml2 XML Schema validation through WebAssembly.

In the proposed design it can be used for two related operations:

  1. validating a profile already selected by root/namespace;
  2. determining the matching profile from registered XSDs when XSD-only matching is enabled.

Schemas and their dependencies should be cached where possible.

Schema dependencies referenced through xsd:include / xsd:import should use an explicit controlled resource resolver.

Validation work should preferably execute outside expensive main-thread rendering work where practical.

Loading and offline behavior

Neither dependency needs to be part of File Viewer's normal initial payload.

A possible loading flow is:

ordinary XML / no configured profiles
    -> existing XML renderer only


rootNamespace enabled
    -> inspect XML
    -> identify candidate profile(s)

xsd enabled
    -> lazy-load XSD validator
    -> validate candidate(s)

profile confirmed
    -> lazy-load XSLT engine
    -> transform
    -> render


failure / ambiguity
    -> existing XML source renderer

All profile resources and WASM/runtime assets should be deployable from the same origin so the feature works in air-gapped, intranet and private deployments.

Required confirmations

  • I searched existing issues and the roadmap for the same request.
  • This request describes one focused user outcome.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions