Free & open source — no account required

v1.2.5-PRICING-19
Web & Frontend • Engineering Documentation

XML to TypeScript Generator

This technical guide provides an in-depth analysis of the xml to typescript engine, best practices for implementation, and data security standards.

XML to TypeScript: XSD Code Generation, Typed Parsing, WSDL Clients, and Zod Validation

Converting XML to TypeScript types has two paths: inferring types from XML samples at development time, or generating types from a formal XSD (XML Schema Definition) at build time. The sample-inference approach is quick but incomplete — it misses optional elements and attribute constraints. XSD-driven code generation is authoritative but requires access to the schema. In practice, most teams combine both: start with a sample to get the rough shape, then validate against the XSD if available. This guide covers both approaches, plus building a typed SOAP client with auto-generated types, and using Zod for runtime XML validation when no XSD is available.

Live Example: Typed fast-xml-parser with Generic Interfaces

import { XMLParser } from 'fast-xml-parser';

// TypeScript interfaces for an enterprise XML response
interface UserAttribute {
  '@_id':     number;
  '@_active': boolean;
  Name:       string;
  Email:      string;
  Role:       string[];   // always an array (isArray config)
  Department?: string;    // optional element
  CreatedAt:  string;     // ISO timestamp as string
}

interface UsersResponse {
  '@_totalCount':  number;
  '@_page':        number;
  '@_pageSize':    number;
  User:            UserAttribute[];
}

interface ApiResponse {
  Users: UsersResponse;
}

// Parser configured to match the TypeScript interface shape
const parser = new XMLParser({
  ignoreAttributes:        false,
  attributeNamePrefix:     '@_',
  attributeValueProcessor: (_name, val) => {
    if (val === 'true')  return true;
    if (val === 'false') return false;
    if (!isNaN(Number(val)) && val !== '') return Number(val);
    return val;
  },
  isArray:    tag => ['User', 'Role'].includes(tag),
  parseTagValue: true,
  removeNSPrefix: true,
  trimValues: true,
});

function parseUsersXml(xml: string): ApiResponse {
  const parsed = parser.parse(xml) as ApiResponse;
  return parsed;
}

const response = parseUsersXml(xmlString);
// response.Users.User is User[] — typed, no casting
console.log(response.Users['@_totalCount']);  // number
console.log(response.Users.User[0].Name);     // string
console.log(response.Users.User[0].Role);     // string[]

XSD to TypeScript Code Generation

// If the XML service has a published XSD, use it to generate authoritative TypeScript types

// Option 1: xsd2ts (simple, fast)
// npm install -D xsd2ts
// npx xsd2ts -i user-service.xsd -o src/types/user-service.ts

// Generated output from xsd2ts:
export interface UserType {
  id:         number;       // xs:integer (required attribute)
  active:     boolean;      // xs:boolean (required attribute)
  Name:       string;       // xs:string (required element)
  Email:      string;       // xs:string (required element)
  Role:       string[];     // xs:string (maxOccurs="unbounded")
  Department: string | undefined;  // xs:string (minOccurs="0")
  CreatedAt:  Date;         // xs:dateTime (required element)
}

// Option 2: xsd-to-ts CLI for complex schemas with extensions, restrictions, etc.
// npm install -D xsd-to-ts
// npx xsd-to-ts user-service.xsd --out src/types/

// Option 3: For WSDL (SOAP service contracts), generate a full client:
// npm install strong-soap
// Generates types AND a callable client from the WSDL URL

// Option 4: For WCF/Java SOAP services — xjc (Java) generates Java classes
// Translate to TypeScript manually or use tools like dtsgenerator

// Build step integration (package.json)
{
  "scripts": {
    "gen:types": "xsd2ts -i schemas/*.xsd -o src/types/generated/",
    "prebuild":  "npm run gen:types"
  }
}

SOAP Service Client with TypeScript

// Typed SOAP client using the 'soap' package
// npm install soap @types/soap

import soap from 'soap';

// Types derived from WSDL (or manually written from documentation)
interface GetUserRequest {
  userId: string;
  includeRoles?: boolean;
}

interface UserResult {
  id:         string;
  name:       string;
  email:      string;
  roles:      { role: string[] };
  department: string;
}

interface GetUserResponse {
  GetUserResult: UserResult;
}

interface UserServiceClient {
  GetUserAsync(args: GetUserRequest): Promise<[GetUserResponse, string, soap.Client]>;
  GetUsersAsync(args: { page: number; pageSize: number }): Promise<[{ Users: UserResult[] }, string]>;
}

// Create typed SOAP client
async function createUserClient(wsdlUrl: string): Promise<soap.Client & UserServiceClient> {
  return soap.createClientAsync(wsdlUrl) as Promise<soap.Client & UserServiceClient>;
}

// Usage
const client = await createUserClient('http://enterprise.internal/user-service?wsdl');

const [response] = await client.GetUserAsync({
  userId:       'USR-001',
  includeRoles: true,
});

const user = response.GetUserResult;
console.log(user.name);          // string — fully typed
console.log(user.roles.role);    // string[] — typed from WSDL

// Automatic SOAP envelope wrapping — you work with plain TypeScript objects,
// the soap library adds the <soap:Envelope><soap:Body>...</soap:Body></soap:Envelope> wrapper

Runtime Validation with Zod (No XSD Available)

// When you have XML samples but no formal schema, use Zod for runtime validation
// after parsing XML to JSON

import { z } from 'zod';
import { XMLParser } from 'fast-xml-parser';

// Define the expected shape with Zod
const UserSchema = z.object({
  '@_id':     z.coerce.number().int().positive(),
  '@_active': z.boolean(),
  Name:       z.string().min(1),
  Email:      z.string().email(),
  Role:       z.array(z.string()),
  Department: z.string().optional(),
  CreatedAt:  z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/),
});

const UsersResponseSchema = z.object({
  '@_totalCount': z.coerce.number(),
  '@_page':       z.coerce.number(),
  User:           z.array(UserSchema),
});

const ApiResponseSchema = z.object({
  Users: UsersResponseSchema,
});

type ApiResponse = z.infer<typeof ApiResponseSchema>;

// Parse + validate pipeline
function parseAndValidate(xml: string): ApiResponse {
  const parser = new XMLParser({
    ignoreAttributes:    false,
    attributeNamePrefix: '@_',
    isArray: tag => ['User', 'Role'].includes(tag),
    removeNSPrefix: true,
    trimValues: true,
  });

  const raw    = parser.parse(xml);
  const result = ApiResponseSchema.safeParse(raw);

  if (!result.success) {
    const errors = result.error.format();
    throw new Error(`XML validation failed: ${JSON.stringify(errors, null, 2)}`);
  }

  return result.data;
}

// If the XML service changes its schema (adds a required field, changes a type),
// the Zod validation catches it immediately at the parse boundary

Type Guards for Parsed XML Data

// Runtime type guard — verify parsed XML matches the expected interface
function isUserResponse(value: unknown): value is ApiResponse {
  if (typeof value !== 'object' || value === null) return false;
  const obj = value as Record<string, unknown>;

  if (!('Users' in obj) || typeof obj.Users !== 'object') return false;
  const users = obj.Users as Record<string, unknown>;

  if (!Array.isArray(users.User)) return false;

  return users.User.every(u =>
    typeof u === 'object' && u !== null &&
    typeof (u as any)['@_id'] === 'number' &&
    typeof (u as any).Name   === 'string' &&
    typeof (u as any).Email  === 'string'
  );
}

// Usage
const raw = parser.parse(xmlString);
if (!isUserResponse(raw)) {
  throw new Error('Unexpected XML response shape — possible schema change');
}
// raw is now typed as ApiResponse
const firstUser = raw.Users.User[0];  // UserAttribute — fully typed

// Assertion function (throws rather than returning boolean)
function assertUserResponse(value: unknown): asserts value is ApiResponse {
  if (!isUserResponse(value)) {
    throw new TypeError(`Expected UserResponse, got: ${JSON.stringify(value).slice(0, 200)}`);
  }
}

Best Practices for Production

  • Generate types from XSD at build time when available: Hand-written interfaces for enterprise XML schemas quickly go out of sync when the service updates. An XSD-to-TypeScript build step re-generates types on every CI run, catching breaking changes at compile time instead of at runtime in production.
  • Configure isArray to match the TypeScript interface exactly: If your interface says Role: string[], the parser must always return an array for that element — even when only one <Role> exists. Mismatched configuration means the TypeScript interface lies: the type says array but at runtime you get a string.
  • Validate parsed XML with Zod when you don't control the service: Third-party or legacy XML services can change their response format without notice. A Zod boundary at parse time surfaces mismatches as structured validation errors, not as mysterious undefined-property errors deep in your business logic.
  • Strip namespace prefixes in the parser, not in the TypeScript types: removeNSPrefix: true in fast-xml-parser means your TypeScript interfaces use clean names like Body instead of 'soap:Body'. Don't model namespaces in TypeScript types unless you're doing round-trip XML generation that requires them.

FAQ

Q: How do I handle XML elements that can be either an object or an array depending on the count?
A: This is the core problem with XML typing. Use isArray: tag => tag === 'YourTag' in fast-xml-parser to force it to always return an array. Then type it as T[] in your TypeScript interface. If you can't change the parser config, use type T = Item | Item[] and unwrap it with a helper: function toArray<T>(v: T | T[]): T[] { return Array.isArray(v) ? v : [v]; }.

Q: How do I generate TypeScript types from a WSDL file?
A: The strong-soap package reads a WSDL URL and generates both a callable client and TypeScript-compatible method definitions. For static generation, openapi-generator-cli supports WSDL-to-TypeScript codegen. If the WSDL is complex (WS-Security, attachments), the zeep Python library generates Python types which you can cross-reference to write TypeScript manually.

Q: What's the performance cost of XML vs JSON parsing in TypeScript?
A: fast-xml-parser processes ~1GB/s on a modern CPU — fast enough for most use cases. JSON parsing via JSON.parse is roughly 5-10x faster on equivalent data because JSON has simpler syntax and browsers/Node.js have native C++ implementations. For high-throughput XML ingestion (>10MB/s), consider a streaming SAX parser (saxes) instead of a DOM-style parser.

Developer FAQ

Is the processing local-only?

Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.

Can I use this for enterprise projects?

Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.