xsd-ui
Schema-driven Angular forms for ISO 20022 message creation.
xsd-ui is an Angular enterprise library that builds a user interface directly from a JSON schema, with pre-built form components that comply with ISO 20022. It renders nested message structures recursively, applies the schema’s own validation rules, and hands back either JSON or XML — so adding a new message type is a schema change rather than a development project.
What it does
- Schema-driven rendering for nested ISO 20022 structures
- Reactive Forms architecture with recursive field generation
- Built-in validation from schema rules — length, pattern, numeric constraints, enum
- XML and JSON patching APIs (setXmlForm, setJsonForm)
- Output shaping with optional empty-field omission
- Event-based API access (onFormReadyEvent) without ViewChild
- Standalone Angular components, ready for library consumption
Install
ng add @angular/material@21npm install xsd-uiBefore you start
xsd-ui does not execute XSD directly. Convert the schema to JSON first — with the xsd-json-converter npm package, or the ISO20022.XSD .NET library.
Register the licence once
xsd-ui is commercially licensed. Register it at application startup rather than per-component, so every form inherits the same entitlement.
import {
configureXsdUiLicense,
XsdLicensePayload,
XsdLicenseValidationResult,
XsdLicenseValidator,
} from 'xsd-ui';
const validateLicenseOnServer: XsdLicenseValidator = async (
licenseKey: string,
payload: XsdLicensePayload,
): Promise<XsdLicenseValidationResult> => {
const response = await fetch('/api/licenses/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey, payload }),
});
if (!response.ok) {
return { valid: false, reason: 'License API unavailable.' };
}
return response.json() as Promise<XsdLicenseValidationResult>;
};
configureXsdUiLicense({
licenseKey: '<your-license-key>',
licenseValidator: validateLicenseOnServer,
requireServerValidation: true,
offlineFallbackOnServerError: true,
});Use the component
import { Component } from '@angular/core';
import {
XsdFormComponent,
XsdLicenseValidationResult,
XsdFormPublicApi,
XsdFormSchema,
} from 'xsd-ui';
@Component({
selector: 'app-payment-form',
standalone: true,
imports: [XsdFormComponent],
template: `
<xsd-form
[schema]="schema"
(onFormReadyEvent)="onFormReady($event)"
(licenseValidationChange)="onLicenseValidationChange($event)"
(change)="onFormChange()"
/>
<button type="button" (click)="submit()">Submit</button>
`,
})
export class PaymentFormComponent {
schema!: XsdFormSchema;
formApi: XsdFormPublicApi | null = null;
onFormReady(api: XsdFormPublicApi): void {
this.formApi = api;
}
onFormChange(): void {
// Live updates, autosave, previews, etc.
}
onLicenseValidationChange(result: XsdLicenseValidationResult): void {
if (!result.valid) {
console.error(result.reason);
}
}
submit(): void {
if (!this.formApi) return;
if (!this.formApi.isValid()) return;
const payload = this.formApi.getForm(true);
console.log(payload);
}
}Public API
Exposed through onFormReadyEvent — no ViewChild required.
export interface XsdFormPublicApi {
isValid(): boolean; // Schema-aware validity (optional empty branches handled)
getForm(omitEmpty?: boolean): any; // Output JSON in root-keyed structure
setJsonForm(json: any): void; // Patch using full root-keyed JSON or inner object
setXmlForm(xml: string): void; // Parse and patch ISO 20022 XML payloads
getNamespace(): string; // Schema namespace
clearForm(): void; // Reset form values
disableForm(): void; // Disable all controls
enableForm(): void; // Enable all controls
}
interface XsdFormSchema {
namespace: string;
schemaElement: SchemaElement | SchemaElement[];
}Core components
- XsdFormComponent (xsd-form) — root orchestration component
- XsdFieldComponent (xsd-field) — recursive field renderer
Inputs
| Input | Type | Required | Description |
|---|---|---|---|
schema | XsdFormSchema | Yes | Root schema including namespace and schemaElement. |
licenseKey | string | No | Override for the globally registered key. |
licenseValidator | XsdLicenseValidator | No | Override for the global server validator. |
requireServerValidation | boolean | No | Override for global server-validation enforcement. |
offlineFallbackOnServerError | boolean | No | Allow offline fallback when server validation errors out. |
translations | TranslationConfig | No | Label and error message overrides. |
Outputs
| Output | Payload | Description |
|---|---|---|
onFormReadyEvent | XsdFormPublicApi | Emits when the form API is ready, and on schema rebuild. |
licenseValidationChange | XsdLicenseValidationResult | Emits licence validation state and failure reason. |
change | void | Emits on value changes and programmatic updates. |
Validations supported
- Required checks from minOccurs
- String length — minLength, maxLength
- Regex pattern
- Numeric boundaries — minInclusive, maxInclusive, minExclusive, maxExclusive
- fractionDigits and totalDigits
- Enumerations (values)
- Choice structures and unbounded arrays
XML and JSON patching
Patch an existing message into the form from either representation.
formApi.setJsonForm({
FIToFICstmrCdtTrf: {
GrpHdr: {
MsgId: 'MSG-001',
CreDtTm: '2026-04-25T12:41:52.673+08:00',
NbOfTxs: '1'
}
}
});formApi.setXmlForm(`
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08">
<FIToFICstmrCdtTrf>
<GrpHdr>
<MsgId>XML-PATCH-001</MsgId>
<NbOfTxs>1</NbOfTxs>
</GrpHdr>
</FIToFICstmrCdtTrf>
</Document>`);Licensing best practice
For browser libraries, client-side checks alone are not tamper-proof. The recommended production setup:
When implementing licenseValidator, return { valid: false } for explicit entitlement rejection — revoked, over-limit, wrong tenant — and throw for network or server failures. That is how xsd-ui tells a deny apart from a fallback.
- Register licensing once at app startup with configureXsdUiLicense(...)
- Keep signed offline key verification enabled — licenseKey plus RSA signature
- Add backend entitlement validation using licenseValidator
- Set requireServerValidation to true in production
- Keep offlineFallbackOnServerError enabled for graceful offline operation
- Return short-lived entitlements from your backend, and rotate or revoke there
Schema element shape
export interface SchemaElement {
id: string;
name: string;
dataType: string;
minOccurs: string;
maxOccurs: string;
minLength: string;
maxLength: string;
pattern: string;
fractionDigits: string;
totalDigits: string;
minInclusive: string;
maxInclusive: string;
values: string[];
isCurrency: boolean;
xpath: string;
expanded: boolean;
elements: SchemaElement[];
}Translation support
Translations key off the name and id properties of a SchemaElement. Declare all rules under an "iso" object.
{
"iso": {
"BkToCstmrStmt": {
"label": "Bank To Customer Statement"
},
"GrpHdr": {
"label": "Group Header"
},
"Document_BkToCstmrStmt_GrpHdr_CreDtTm": {
"label": "Create Datetime",
"general": {
"format": "YYYY-MM-DDThh:mm:ss.sss+/-"
},
"error": {
"required": "This field is required"
}
}
}
}Running this in production?
We wrote these libraries and we run them. If you are planning an ISO 20022 migration, we can tell you quickly where the hard parts actually are.