JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 24
  • Score
    100M100P100Q86854F
  • License MIT

TypeScript XML serialization library with decorator-based metadata. Supports namespaces, custom converters, validation, wrapped/unwrapped arrays, and bidirectional XML-object mapping.

Package Exports

  • @cerios/xml-poto
  • @cerios/xml-poto/package.json

Readme

@cerios/xml-poto

A powerful TypeScript XML serialization library with decorator-based metadata. Provides type-safe, bidirectional XML-object mapping with support for namespaces, custom converters, validation, and flexible array handling.

npm version npm downloads License: MIT TypeScript

✨ Key Features

  • 🎯 Type-Safe - Full TypeScript support with compile-time validation
  • 🔄 Bidirectional - Seamless XML ↔ Object conversion
  • 🏷️ Decorator-Based - Clean, declarative syntax
  • 🔍 Powerful Query API - XPath-like querying with fluent interface
  • 🌐 Namespace Support - Complete XML namespace handling
  • Validation - Pattern matching, enums, and required fields
  • 🔧 Extensible - Custom converters and transformations
  • 📦 Zero Config - Sensible defaults, extensive customization

📦 Installation

npm install @cerios/xml-poto

As a Dev Dependency

npm install --save-dev @cerios/xml-poto

Note: This package uses standard TypeScript decorators and does not require experimentalDecorators or emitDecoratorMetadata in your tsconfig.json. It works with modern TypeScript configurations out of the box.

🎯 Quick Start

import { XmlRoot, XmlElement, XmlAttribute, XmlSerializer } from '@cerios/xml-poto';

// 1. Define your class with decorators
@XmlRoot({ elementName: 'Person' })
class Person {
    @XmlAttribute({ name: 'id' })
    id: string = '';

    @XmlElement({ name: 'Name' })
    name: string = '';

    @XmlElement({ name: 'Email' })
    email: string = '';

    @XmlElement({ name: 'Age' })
    age?: number;
}

// 2. Create serializer
const serializer = new XmlSerializer();

// 3. Serialize to XML
const person = new Person();
person.id = '123';
person.name = 'John Doe';
person.email = 'john@example.com';
person.age = 30;

const xml = serializer.toXml(person);
console.log(xml);
// Output:
// <?xml version="1.0" encoding="UTF-8"?>
// <Person id="123">
//   <Name>John Doe</Name>
//   <Email>john@example.com</Email>
//   <Age>30</Age>
// </Person>

// 4. Deserialize from XML
const xmlString = `
    <Person id="456">
        <Name>Jane Smith</Name>
        <Email>jane@example.com</Email>
        <Age>25</Age>
    </Person>
`;

const deserializedPerson = serializer.fromXml(xmlString, Person);
console.log(deserializedPerson);
// Output: Person { id: '456', name: 'Jane Smith', email: 'jane@example.com', age: 25 }

📖 Documentation

Getting Started

Core Features

Advanced Features

Reference

Examples

🎯 Common Use Cases

Use Case Feature Documentation
REST API XML responses Basic serialization Getting Started
Configuration files Nested objects, validation Nested Objects
RSS/Atom feeds Unwrapped arrays Arrays
SOAP services Namespaces Namespaces
Blog content Mixed content, CDATA Mixed Content
Data extraction Query API, XPath Querying
Code documentation CDATA, comments CDATA

🔧 Decorator Overview

Decorator Purpose Example
@XmlRoot Define root element @XmlRoot({ elementName: 'Person' })
@XmlElement Map to element @XmlElement({ name: 'Name' })
@XmlAttribute Map to attribute @XmlAttribute({ name: 'id' })
@XmlText Map to text content @XmlText()
@XmlComment Add XML comments @XmlComment()
@XmlArrayItem Configure arrays @XmlArrayItem({ itemName: 'Item' })
@XmlQueryable Enable query API @XmlQueryable()

Full API Reference →

💡 Why xml-poto?

Traditional Approach ❌

// Manual XML construction - error-prone
const xml = `<Person id="${id}"><Name>${name}</Name></Person>`;

// Manual parsing - tedious
const parser = new DOMParser();
const doc = parser.parseFromString(xml, 'text/xml');
const name = doc.querySelector('Name')?.textContent;

With xml-poto ✅

// Type-safe, automatic, validated
const xml = serializer.toXml(person);
const person = serializer.fromXml(xml, Person);

Benefits:

  • ✅ Type safety at compile-time
  • ✅ Automatic validation
  • ✅ No string concatenation
  • ✅ Bidirectional mapping
  • ✅ IDE autocomplete

📝 Feature Highlights

Query API - Extract Data with Ease

@XmlRoot({ elementName: 'Catalog' })
class Catalog {
    @XmlQueryable()  // Lazy-loaded and cached by default
    query!: QueryableElement;
}

const catalog = serializer.fromXml(xmlString, Catalog);

// Use XPath-like queries (QueryableElement built on first access)
const titles = catalog.query.find('Product').find('Title').texts();
const expensiveItems = catalog.query
    .find('Product')
    .whereValueGreaterThan(100);

// Navigate the tree
const parent = catalog.query.children[0].parent;
const siblings = catalog.query.children[0].siblings;

Learn more about Querying →

Arrays - Flexible Collection Handling

// Wrapped array
@XmlArrayItem({ containerName: 'Books', itemName: 'Book', type: Book })
books: Book[] = [];
// <Books><Book>...</Book><Book>...</Book></Books>

// Unwrapped array
@XmlArrayItem({ itemName: 'Item', type: Item })
items: Item[] = [];
// <Item>...</Item><Item>...</Item>

Learn more about Arrays →

Namespaces - Full XML Namespace Support

const ns = { uri: 'http://example.com/schema', prefix: 'ex' };

@XmlRoot({ elementName: 'Document', namespace: ns })
class Document {
    @XmlElement({ name: 'Title', namespace: ns })
    title: string = '';
}
// <ex:Document xmlns:ex="http://example.com/schema">
//   <ex:Title>...</ex:Title>
// </ex:Document>

Learn more about Namespaces →

Mixed Content - HTML-like Structures

@XmlRoot({ elementName: 'Article' })
class Article {
    @XmlElement({ name: 'Content', mixedContent: true })
    content: any;
}
// Handles: <Content>Text <em>emphasis</em> more text</Content>

Learn more about Mixed Content →

Validation - Enforce Data Integrity

@XmlAttribute({
    name: 'email',
    required: true,
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
})
email: string = '';

@XmlElement({
    name: 'status',
    enum: ['active', 'inactive', 'pending']
})
status: string = '';

Learn more about Validation →

Custom Converters - Transform Values

const dateConverter = {
    serialize: (date: Date) => date.toISOString(),
    deserialize: (str: string) => new Date(str)
};

@XmlElement({ name: 'CreatedAt', converter: dateConverter })
createdAt: Date = new Date();

Learn more about Converters →

🎓 Best Practices

  1. Initialize properties: Always provide default values

    name: string = '';  // ✅ Good
    name: string;       // ❌ May cause issues
  2. Specify types for arrays: Use the type parameter for complex objects

    @XmlArrayItem({ itemName: 'Item', type: Item })
    items: Item[] = [];
  3. Use validation for external data: Apply required, pattern, enum for untrusted XML

    @XmlAttribute({ name: 'id', required: true, pattern: /^\d+$/ })
    id: string = '';
  4. Test round-trip serialization: Verify data integrity

    const xml = serializer.toXml(original);
    const restored = serializer.fromXml(xml, MyClass);

🆚 Comparison

Feature xml-poto Manual Parsing Other Libraries
Type Safety ✅ Full ❌ None ⚠️ Partial
Bidirectional ✅ Yes ❌ No ✅ Yes
Decorators ✅ Yes ❌ No ⚠️ Some
Query API ✅ XPath-like ❌ No ❌ No
Namespaces ✅ Full ⚠️ Manual ⚠️ Limited
Validation ✅ Built-in ❌ Manual ⚠️ External
Mixed Content ✅ Yes ⚠️ Complex ❌ No

🛠️ Advanced Topics

🤝 Contributing

Contributions are welcome! Please see our Contributing Guide.

📄 License

MIT © Ronald Veth - Cerios


Next Steps: