xml2js
See the test/test.js
for exhaustive samples.
Usage
const xml_build = require('@developpement/xml2js').build;
await xml_build({ ... }, '<some><xml/></some>');
Features
- Extract any text from XML to most of the json types (string, boolean, numbers, etc.).
- Shortcuts to allow consise extraction.
- Callback to custom & complexes extractions (such as date conversions etc.).
- Protect against some errors during extraction.
- Verifies paths for any non-collection types.
- Verifies json keys (allows
type
, path
, end
, empty
and _d_...
for debugging).
- Full asynchronous
Keys allowed
- type:
string
, array
, object
, int
, float
, boolean
, callback
, raw
(default string
).
Add an extra ?
to make it nullable (if not found, return null).
Add an extra []
to make it an array of this type. - path: the XPATH of the context to fetch (default none).
- end:
text
, attr@name
(default text
). - empty:
true
, false
(default false
). Raise an error if false
and the array empty.
Build an attribute
build({
type: "string",
path: "//item",
end: "attr@id"
}, xml)
build("//a/b|attr@id", xml)
build({
type: "string?",
path: "//a/maybesomething",
end: "attr@id"
}, xml)
Build a text
build({
type: "string",
path: "//item",
end: "text"
}, xml)
Build an array
build({
type: "array",
path: "//items/item",
end: {
type: "string",
end: "attr@id"
}
}, xml)
You can also refuse empty array with empty: false
.
build({
type: "array",
empty: false,
path: "//items/not-item",
end: {
type: "string",
end: "attr@id"
}
}, xml)
Build an object
build({
type: "object",
path: "//item",
end: {
b: {
type: "string",
end: "text"
}
}
}, xml)
Build some types (int, float, boolean)
build({
type: "object",
end: {
int: {
type: "int",
path: "//int",
end: "text"
},
float: {
type: "float",
path: "//float",
end: "text"
},
bool: {
type: "boolean",
path: "//bool",
end: "text"
}
}
}, xml)
Build an element OR another
build({
type: "object",
end: {
or: {
type: "or",
end: [{
type: "string",
end: "text",
path: "/a/notdefined"
},
{
type: "string",
end: "text",
path: "/a/defined"
}
]
}
}
}, xml)
Note: you can use the key empty: false
on array to refuse empty ones.
Build a callback (custom type)
This type of leaf allows you to build a complex/custom value.
function callback(json, context) {
return context.text() + " => custom stuff"
}
build({
type: "callback",
path: "/a/defined",
end: callback
}, xml)
It fully allows async functions.
In the callback, there are 2 parameters:
Most of the time, we only use the context to get the context.text()
or context.attr("AttributeName").value()
and then change this value.
Sample:
<Price Decimals="2" Value="1250" />
function callback(json, context) {
const divider = 10**Number(context.attr("Decimals").value());
const value = Number(context.attr("Value")) / divider;
return value;
}
build({
type: "callback",
path: "/Price",
end: callback
}, xml)
Not build a xml data
This type of leaf allows you to build a complex/custom value.
build({
type: "raw",
path: "/a/raw",
}, xml)
Syntactic sugar
There is syntactic sugar for arrays: string[]
will be treated as an array of strings.
build({
type: "object[]",
path: "X",
end: {
str: {
type: "string",
path: "X/Y",
end: "text"
}
}
}, xml)
build((json, context) => context.get('stuff').text() + context.get('things').text(), xml)
Note that it is not possible to make a multi dimensional array with this shortcut.
Default values
Each of the three values type
, path
and end
have default values. Here they are:
{
type: "string",
path: "",
end: "text"
}
If a string is provided instead of an object, it will be understood as the path of the element. That means that {a: 'X'}
is equivalent to the following:
{
a: {
type: "string",
path: "X",
end: "text"
}
}
A side effect of those default values is that the type []
means string[]
.
Big sample
build({
type: "object",
path: "root"
end: {
uuid: { type: "string", path: "uuid", end: "text" },
data: {
type: "array",
path: "dataList/data",
end: {
type: "object",
end: {
time: { type: "int", end: "attr@timestamp" },
duration: { type: "float", end: "attr@duration" },
output: { type: "string", end: "text", path: "stdout" },
success: { type: "boolean", end: "attr@success" },
orTest: {
type: "or",
end: [
{type: "string", end: "text", path: "maybeDefined1"},
{type: "string", end: "text", path: "maybeDefined2"},
{type: "string", end: "text", path: "maybeDefined3"}
]
}
}
}
}
}
}, xmlDocument)
Namespaces
If you use namespace, you have to send them as 3rd argument to build()
.
build({...}, { 'namespace': 'http://url.to/namespace/declaration' });
If you search to parse a xml with a xmlns=...
declaration, you may need to write a weird XPath insead of the usual way:
xml2js.build('//elemName', xml)
replaced by
xml2js.build("//*[local-name()='elemName']", xml)
ref