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.
✨ 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-potoAs a Dev Dependency
npm install --save-dev @cerios/xml-potoNote: This package uses standard TypeScript decorators and does not require
experimentalDecoratorsoremitDecoratorMetadatain yourtsconfig.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
- Elements & Attributes - Basic XML mapping
- Text Content - Text nodes and CDATA
- Arrays & Collections - Wrapped and unwrapped arrays
- Nested Objects - Complex hierarchies
- Namespaces - XML namespace support
- Querying XML - XPath-like queries and data extraction
Advanced Features
- Advanced Type Handling -
xsi:nil,xsi:type, union types - Mixed Content - HTML-like content
- Custom Converters - Value transformations
- Validation - Patterns, enums, required fields
- CDATA Sections - Preserve special characters
- XML Comments - Documentation in XML
Reference
- API Reference - Complete API documentation
- Decorator Reference
- Serialization Options
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() |
💡 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;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>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>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 = '';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();🎓 Best Practices
Initialize properties: Always provide default values
name: string = ''; // ✅ Good name: string; // ❌ May cause issues
Specify types for arrays: Use the
typeparameter for complex objects@XmlArrayItem({ itemName: 'Item', type: Item }) items: Item[] = [];
Use validation for external data: Apply
required,pattern,enumfor untrusted XML@XmlAttribute({ name: 'id', required: true, pattern: /^\d+$/ }) id: string = '';
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
🔗 Links
Next Steps: