util.datamapper

Given a declarative description of some data, lets you extract that data from an XML stanza, or turn such data into an XML stanza.

Added in Prosody trunk.

Introduction

As with all utils, to use util.datamapper you have to import it first, by adding this line near the top of your code:

local datamapper = require "util.datamapper";

Say you have this piece of XML (from XEP-0092):

<query xmlns="jabber:iq:version">
  <name>Prosody</name>
  <version>0.11.8</version>
  <os>Linux</os>
</query>

Just looking at it, it looks like a record, dict, object (loved child has many names) with three fields. We can describe it using a variant of JSON Schema with an extension for XML support from the OpenAPI specification, like this:

local = {
    type = "object";
    xml = {
        name = "query";
        namespace = "jabber:iq:version";
    };
    properties = {
        name = "string";
        version = "string";
        os = "string";
    };
};

Now you can extract those fields into a convenient table using the parse function.

data = datamapper.parse(schema, stanza)

The data variable should now hold a table like this:

data = {
    name="Prosody";
    version="0.11.8";
    os="Linux";
}

If you want to turn the data back into XML, you can do that using the unparse function

stanza = datamapper.unparse(schema, data)

Of course, the above could instead have been written like this:

-- parse-equivalent
data = {
    name = stanza:get_child_text("name");
    version = stanza:get_child_text("version");
    os = stanza:get_child_text("os");
}

-- unparse-equivalent
stanza = st.stanza("query", { xmlns = "jabber:iq:version" })
    :text_tag("name", "Prosody")
    :text_tag("version", "0.11.8")
    :text_tag("os", "Linux")
:reset();

For such a simple case as this, you probably should, util.datamapper is a pretty big and complex library. But the more complex XML you have to deal with, the more complicated code it will spare you from writing. Especially when dealing with deeply nested trees of optional elements, where you have to check whether each child element really was included or risk an attempt to index a nil value error.

More (and more complicated) examples can be found for example in the test cases for util.datamapper

Reference

schema

Example

local = {
    type = "object";
    xml = {
        name = "query";
        namespace = "jabber:iq:version";
    };
    properties = {
        name = "string";
        version = "string";
        os = "string";
    };
};

This describes an XML stanza (from XEP-0092) looking like this:

<query xmlns="jabber:iq:version">
  <name>Prosody</name>
  <version>0.11.8</version>
  <os>Linux</os>
</query>

parse

data = datamapper.parse(schema, stanza)
-- This might give you a table like:
data = {
    name="Prosody";
    version="0.11.8";
    os="Linux";
}

unparse

stanza = datamapper.unparse(schema, data)
-- would give you back the XML snippet from earlier