@cerios/xml-poto
v2.1.0
Published
TypeScript XML serialization library with decorator-based metadata. Supports namespaces, custom converters, validation, wrapped/unwrapped arrays, and bidirectional XML-object mapping.
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
- ✏️ Dynamic XML Manipulation - Add, update, delete elements at runtime
- 🔁 Full Serialization - Parse, modify, and serialize back to XML
- 🌐 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 = "[email protected]";
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>[email protected]</Email>
// <Age>30</Age>
// </Person>
// 4. Deserialize from XML
const xmlString = `
<Person id="456">
<Name>Jane Smith</Name>
<Email>[email protected]</Email>
<Age>25</Age>
</Person>
`;
const deserializedPerson = serializer.fromXml(xmlString, Person);
console.log(deserializedPerson);
// Output: Person { id: '456', name: 'Jane Smith', email: '[email protected]', age: 25 }🔁 Bi-directional XML (XmlDynamic)
Parse XML, modify it dynamically, and serialize back - perfect for XML transformation workflows:
import { XmlRoot, XmlDynamic, DynamicElement, XmlQuery, XmlSerializer } from "@cerios/xml-poto";
@XmlRoot({ elementName: "Catalog" })
class Catalog {
@XmlDynamic()
dynamic!: DynamicElement;
}
const xml = `
<Catalog>
<Product id="1"><Name>Laptop</Name><Price>999</Price></Product>
<Product id="2"><Name>Mouse</Name><Price>29</Price></Product>
</Catalog>
`;
const catalog = serializer.fromXml(xml, Catalog);
// Query and modify
const query = new XmlQuery([catalog.dynamic]);
query.find("Product").whereValueGreaterThan(100).setAttr("premium", "true");
// Add new elements
catalog.dynamic
.createChild({
name: "Product",
attributes: { id: "3" },
})
.createChild({ name: "Name", text: "Keyboard" });
// Serialize back to XML
const updatedXml = catalog.dynamic.toXml({ indent: " " });See Bi-directional XML Guide for complete documentation.
Note: Use
DynamicElementand@XmlDynamicfor new code.DynamicElementand@XmlDynamicare deprecated but still supported.
📖 Documentation
Getting Started
Core Features
- Elements & Attributes - Basic XML mapping
- Text Content - Text nodes and CDATA
- Arrays & Collections - Wrapped and unwrapped arrays
- Bi-directional XML (XmlDynamic) - ⭐ Parse, modify, and serialize XML
- XBRL Support - 🏦 Financial reporting with XBRL
- 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() |
| @XmlArray | Configure arrays | @XmlArray({ itemName: 'Item' }) |
| @XmlDynamic | Enable query API | @XmlDynamic() |
💡 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 {
@XmlDynamic() // Lazy-loaded and cached by default
query!: DynamicElement;
}
const catalog = serializer.fromXml(xmlString, Catalog);
// Use XPath-like queries (DynamicElement 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
@XmlArray({ containerName: 'Books', itemName: 'Book', type: Book })
books: Book[] = [];
// <Books><Book>...</Book><Book>...</Book></Books>
// Unwrapped array
@XmlArray({ 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 issuesSpecify types for arrays: Use the
typeparameter for complex objects@XmlArray({ 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:
