jml-tools
v0.1.3
Published
A set of tools to simplify the usage of XML-based JSON markup (JML) in JavaScript
Downloads
3
Readme
JML Tools
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.
Usage
Installation
npm install jml-tools --save
or
yarn add jml-tools
Quick start
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"
// }
// ]
// }
Running tests
npm test
API Reference
This library provides the following functionalities:
create
Manually creates a JML object that is fully compatible with xml-js.
Syntax
create(name[, data])
Parameters
name
A string, either prefixed or not that will be the name of the object to be created.
data
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'}
|
Usage example
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"
// }
// ]
// }
evaluate
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.
Syntax
evaluate(path, jmlObject[, options])
Parameters
path
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.
Examples
| 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 |
Notable differences to XPath
- 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]
.
jmlObject
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.
options
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'}]
|
Usage example
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']
serialize
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.
Syntax
serialize(jmlObject, mappings[, options])
Parameters
jmlObject
A valid JML object as created by the create method and xml-js. It will be serialized according to the provided mappings.
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.|
options
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
|
Usage example
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>
Authors
- Daniel Jeller
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
Thanks to @nashwaan for his excellent xml-js library.