This library is aimed at providing a set of tools to work with XML-based markup JSON data similar to (but not the same as) JSON-ML in JavaScript. The "markup language" (JML) is defined by the non-compact JSON structure proposed by the xml-js library. It can be converted lossless to and from XML documents including mixed-content elements. The tools are fully namespace aware.
npm install jml-tools --save
or
yarn add jml-tools
import {create} from 'jml-tools';
const name = 'person';
const data = {content: 'Freddie Mercury'};
const jmlObject = create(name, data);
console.log(jmlObject);
// Output:
// {
// "elements": [
// {
// "elements": [
// {
// "text": "Freddie Mercury",
// "type": "text"
// }
// ],
// "name": "person",
// "type": "element"
// }
// ]
// }
npm test
This library provides the following functionalities:
Manually creates a JML object that is fully compatible with xml-js.
create(name[, data])
A string, either prefixed or not that will be the name of the object to be created.
An optional data object that can have any or all of the following properties:
Property | Description | Examples |
---|---|---|
namespaces | An object array describing the objects' namespaces with the namespace URI and an optional prefix both as string values. A namespace with only an URI is considered as the objects' default namespace similar to XML. | [{prefix: 'ns', uri: 'http://example.com/ns'}] [{uri: 'http://example.com/default'}, {prefix: 'ns', 'uri: 'http://example.org/ns'}] |
content | The content (if any). Can be a string for pure text content, a single JML object or an array of JML objects. | 'Freddie Mercury' |
attributes | All attributes as an object of string key-value pairs. The values will be converted to strings if necessary. | {date: '2017-12-31', time: '23:12'} |
import {create} from 'jml-tools';
const name = 'person';
const data = {content: 'Freddie Mercury'};
const jmlObject = create(name, data);
console.log(jmlObject);
// Output:
// {
// "elements": [
// {
// "elements": [
// {
// "text": "Freddie Mercury",
// "type": "text"
// }
// ],
// "name": "person",
// "type": "element"
// }
// ]
// }
Evaluates a path expression similar to XPath on a JML object starting with the content of the root object. It doesn't support all of the functionalities of XPath, most importantly, while the descendant axis is available in the form of the double forward slash //
, the other axes are not.
evaluate(path, jmlObject[, options])
A string that describes the individual steps that form the path to evaluate on a JML object. The steps are made up of (optionally qualified) object names, text()
to select text content or @name
to select an object attribute. Each step can have zero or more conditions that describe which specific objects it will match.
Path | Result |
---|---|
/first/second |
All second objects that are direct children of a first object |
//second/text() |
The direct text content of all second objects |
//second//text() |
The text content of all second objects and their descendants |
/first/@value |
The text content of the value attribute of all first objects |
/first[@value="1"] |
All first objects that have a value attribute with the value 1 . The value can be a string or a number |
/first[.//third/text()="text"] |
All first objects that have third descendants that have text as direct text content. The path that specifies the condition value starts with descendants of the current (e.g. first ) object and is governed by the same rules than the outer path |
/first[2]/second |
All second objects that are a child of the second first object |
/ns:first |
All first objects that are in the ns namespace. The ns namespace needs to be declared in the methods' options object |
- The value of an attribute can be accessed directly by
/something/@attribute
instead ofdata(/something/@attribute)
or/something/@attribute/string()
- When evaluating on an object that is equivalent to
<persons><person><name><given>Freddy</given></name></person><person><name><given>Brian</given></name></person></persons>
,//given[1]
returns the first of all given objects, wherever they appear, instead of the first given object in each individual context like in XPath. A functionality similar to the default in XPath behaviour can be achieved by//name/given[1]
.
A valid JML object as created by the create method and xml-js. The path will be evaluated on its contents starting inside the root object.
An optional object that describes the options for the evaluation:
Property | Description | Examples |
---|---|---|
namespaces | An object array describing the namespaces used in the path with their namespace URI and prefix, both as string values. Namespaces that appear as default values in the target JML object still need to be declared with a prefix in the options object. | [{prefix: 'ns1', uri: 'http://example.com/ns1'}, {prefix: 'ns2', uri: 'http://example.com/ns2'}] |
import {evaluate} from 'jml-tools';
// original XML:
// <paragraph xmlns="http://example.com/ns">JSON is just as <emphasized>fun</emphasized> as XML.</paragraph>
const jmlObject = {
elements: [{
type: 'element',
name: 'paragraph',
attributes: {
xmlns: 'http://example.com/ns'
},
elements: [
{type: 'text', text: 'JSON is just as '},
{
type: 'element',
name: 'emphasized',
elements: [
{type: 'text', text: 'fun'}
]
},
{type: 'text', text: ' as XML.'}
]
}]
};
const result = evaluate('/ns:emphasized/text()', jmlObject, {namespaces: [{prefix: 'ns', uri: 'http://example.com/ns'}]});
console.log(result);
// Output:
// ['fun']
Serializes a JML object to a string according to fully qualified mappings that target either the whole object or individual child objects. This can, for example, be used to transform arbitrary structures into HTML or a different JSON representation.
serialize(jmlObject, mappings[, options])
A valid JML object as created by the create method and xml-js. It will be serialized according to the provided mappings.
The rules to apply to the jmlObject. It describes how its contents will be serialized. It can any of the following:
- An object consisting of string key-value pairs describing the mapping into serialized XML. An asterisk (
*
) as key is interpreted as the default mapping to use if no other mapping matches. - An object consisting of key-key value pairs similar to option 1 above with the difference that the value is not a string but a function that should return the transformed content as a string. The functions' signature is
function({name, contents, attributes})
. - A function that will be applied to each child object (similar to the child elements in XML). The functions' signature is
function({name, contents, attributes})
.
Note 1: The options 1 and 2 can be mixed with each other. It is possible to map some keys to string values and some to functions.
Note 2: If the object to be match by options 1 and 2 above are qualified, the objects' namespace needs to be declared in the options object and the correct prefixes need to be added to the keys to match the specific objects.
Example | Description |
---|---|
({content, name}) => `<span data-name="${name}">${content}</span>`) |
A single function is applied to the object and all its child objects. It maps the content into span elements with the objects' names as data-name attributes. |
{'ns:paragraph': 'p', 'bold': 'b', '*': 'span'} |
This mapping object matches all paragraph objects in the namespace prefixed with ns and unqualified bold objects. All other, unmapped, objects are matched by the asterisk. All string values describe simple XML element names to wrap the objects' contents into (see the usage example). |
{'ns:paragraph': 'p', 'bold': ({content}) => content} |
This mapping object is similar to the preceding one with the difference that the mapping for the bold key is described by a function that only returns the content for each object without wrapping it into something. The other difference is that the asterisk-default mapping is missing. This way, all objects not matched by paragraph and bold will be ignored. |
An optional object that describes the options for the serialization:
Property | Description | Examples |
---|---|---|
namespaces | An object array describing namespaces used in the mapping with their namespace URI and prefix, both as string values. Namespaces that appear as default values in the target JML object still need to be declared with a prefix in the options object. | [{prefix: 'ns', uri: 'http://example.com/ns'}] [{uri: 'http://example.com/default'}, {prefix: 'ns', 'uri: 'http://example.org/ns'}] |
skipEmpty | A boolean that sets whether or not to skip empty objects during the serialization. Defaults to false. | true |
import {serialize} from 'jml-tools';
// original XML:
// <paragraph xmlns="http://example.com/ns">JSON is just as <emphasized>fun</emphasized> as XML.</paragraph>
const jmlObject = {
elements: [{
type: 'element',
name: 'paragraph',
attributes: {
xmlns: 'http://example.com/ns'
},
elements: [
{type: 'text', text: 'JSON is just as '},
{
type: 'element',
name: 'emphasized',
elements: [
{type: 'text', text: 'fun'}
]
},
{type: 'text', text: ' as XML.'}
]
}]
};
const mappings = {
'ns:paragraph': 'p',
'ns:emphasized': 'i'
};
const options = {
namespaces: [{
prefix: 'ns',
uri: 'http://example.com/ns'
}]
};
const serialized = serialize(jmlObject, mappings, options);
console.log(serialized);
// Output:
// <p>JSON is just as <i>fun</i> as XML.</p>
- Daniel Jeller
This project is licensed under the MIT License - see the LICENSE file for details.
Thanks to @nashwaan for his excellent xml-js library.