A Dart library for mapping XML data to Dart objects using annotations. Inspired by JSON serialization patterns, this package eliminates boilerplate XML parsing code.
- Declarative annotations for XML elements and attributes
- Automatic type conversion and validation
- Support for nested objects and collections
- Custom type converters
- Null safety support
Add to your pubspec.yaml:
dependencies:
xml: ^6.0.0xml_object_mapping: ^1.0.0dev_dependencies:
xml_object_mapping_generator: ^1.0.0Define your model class with annotations:
<userid="123">
<nameSurname>...</nameSurname>
<email>...</email>
<maxItems>9</maxItems>
<status>PROCESSING</status>
</user>import"package:xml_object_mapping/xml_object_mapping.dart";
part"user.g.dart";
@xmlMapclassUser {
@xmlMapAttributefinalint id;
@XmlMapElement(overrideName:"nameSurname")
finalString name;
@xmlMapElementfinalString email;
@xmlMapElementfinalString? phone;
@xmlMapElementfinalint maxItems;
@xmlMapElementfinalOrderStatus status;
User({
requiredthis.id,
requiredthis.name,
requiredthis.email,
requiredthis.status,
this.phone,
this.maxItems =10,
});
}
enumOrderStatus { PENDING, PROCESSING, SHIPPED, DELIVERED }Run the build runner to generate the mapper:
dart run build_runner buildThis generates UserXmlMapper. Use it to parse XML from various sources:
voidmain() {
var user =UserXmlMapper.parse(path:'data/user.xml');
user =UserXmlMapper.parse(file:File('data/user.xml'));
user =UserXmlMapper.parse(text:"<user>...<user/>");
user =UserXmlMapper.parse(xmlElement: xmlElement);
}| Annotation | Description |
|---|---|
@XmlMap | Marks a class for XML mapping code generation |
@XmlMapValue({XmlConverter? decorator, XmlConverter? converter}) | Maps the content of an XML element to a field |
@XmlMapElement({String? overrideName, XmlConverter? decorator, XmlConverter? converter}) | Maps an XML element to a field |
@XmlMapAttribute({String? overrideName, XmlConverter? decorator, XmlConverter? converter}) | Maps an XML attribute to a field |
@XmlMapList({String? overrideName, String? childName, XmlConverter? converter}) | Maps repeated elements to a List field |
The following built-in types are supported out of the box:
| Type | Description |
|---|---|
String | Text content |
int | Integer numbers |
double | Floating-point numbers |
num | Any numeric type |
bool | Boolean values (true/false) |
DateTime | ISO 8601 date-time strings |
For other types, use custom converters (see below).
<company>
<name>...</name>
<address>
<street>...</street>
<city>...</city>
</address>
</company>@xmlMapclassCompany {
@xmlMapElementfinalString name;
@xmlMapElementfinalAddress address;
Company({requiredthis.name, requiredthis.address});
}
@xmlMapclassAddress {
@xmlMapElementfinalString street;
@xmlMapElementfinalString city;
Address({requiredthis.street, requiredthis.city});
}<library>
<books>
<bookName>...</bookName>
<bookName>...</bookName>
</books>
</library>@xmlMapclassLibrary {
@XmlMapList(childName:'bookName')
finalList<String> books;
Library({requiredthis.books});
}<bookstore>
<books>
<book>
<title>...</title>
<author>...</author>
<price>...</price>
</book>
<book>
<title>...</title>
<author>...</author>
<price>...</price>
</book>
</books>
</bookstore>@xmlMapclassBookstore {
@XmlMapList(childName:'book')
finalList<Book> books;
Bookstore({requiredthis.books});
}
@xmlMapclassBook {
@xmlMapElementfinalString title;
@xmlMapElementfinalString author;
@xmlMapElementfinaldouble price;
Book({requiredthis.title, requiredthis.author, requiredthis.price});
}<product>
<price>$19.99</price>
</product>@xmlMapclassProduct {
@XmlMapElement(converter:PriceConverter())
finaldouble price;
Product({requiredthis.price});
}
classPriceConverterimplementsXmlConverter<double> {
constPriceConverter();
@overridedoubleconvert(String value) =>double.parse(value.replaceAll(r"$", ""));
}Use decorators to process the raw XML text value before it's converted. This is useful when the XML contains values in a different format than your Dart type expects:
<invoice>
<netPremium>12312,9900</netPremium>
</invoice>classDecimalPointDecoratorimplementsXmlConverter<String> {
constDecimalPointDecorator();
@overrideStringconvert(String value) => value.replaceAll(',', '.');
}
@xmlMapclassInvoice {
@XmlMapElement(decorator:DecimalPointDecorator())
finaldouble netPremium;
Invoice({requiredthis.netPremium});
}The generated code will apply the decorator first, then convert:
final elem_netPremium = element.getElement("netPremium");
final netPremium = elem_netPremium !=null? (constDoubleConverter().convert(
constDecimalPointDecorator().convert(elem_netPremium.text),
) asdouble?)
:null;Use @XmlMapList with a map-like structure for key-value pairs:
<config>
<properties>
<entrykey="timeout">30</entry>
<entrykey="retries">3</entry>
</properties>
</config>@xmlMapclassConfig {
@XmlMapList(childName:'entry')
finalList<PropertyEntry> properties;
Config({requiredthis.properties});
}
@xmlMapclassPropertyEntry {
@xmlMapAttributefinalString key;
@xmlMapValuefinalint value;
PropertyEntry({requiredthis.key, requiredthis.value});
}
voidmain() {
final config =XmlConfigMapper.parse(text: xml);
final map = {for (var e in config.properties) e.key: e.value};
}Convert objects back to XML:
voidmain() {
final user =User(id:'123', name:'John Doe', email:'john@example.com');
final xmlElement =UserXmlMapper.toXml(user);
}The mapper throws specific exceptions for common errors:
voidmain() {
try {
final user =UserXmlMapper.parse(text: xmlString);
} onXmlMappingExceptioncatch (e) {
print('Mapping error: ${e.message}');
} onXmlFormatExceptioncatch (e) {
print('Format error: ${e.message}');
} onXmlParserExceptioncatch (e) {
print('Parse error: ${e.message}');
}
}