SkillAgentSearch skills...

Xembly

Assembly for XML: an imperative language for creating and modifying XML documents (and a Java library)

Install / Use

/learn @yegor256/Xembly
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

XML Modifying Imperative Language (and Java Lib)

EO principles respected here DevOps By Rultor.com We recommend IntelliJ IDEA

mvn PDD status codecov codebeat badge Codacy Badge Javadoc Maven Central Hits-of-Code Lines-of-Code Code Smells

Xembly is an [Assembly]-like [imperative] programming language for data manipulation in XML documents. It is a much simpler alternative to [DOM], [XSLT], and [XQuery]. Read this blog post for a more detailed explanation: [Xembly, an Assembly for XML][blog]. You may also want to watch this webinar.

You need this dependency:

<dependency>
  <groupId>com.jcabi.incubator</groupId>
  <artifactId>xembly</artifactId>
  <version>0.32.2</version>
</dependency>

Here is a command line implementation (as Ruby gem): [xembly-gem].

For example, you have an XML document:

<orders>
  <order id="553">
    <amount>$45.00</amount>
  </order>
</orders>

Then, you want to change the amount of the order #553 from $45.00 to $140.00. Xembly script would look like this:

XPATH "orders/order[@id=553]";
XPATH "amount";
SET "$140.00";

As you see, it's much simpler and compact than [DOM], [XSLT], or [XQuery].

This Java package implements Xembly:

Document document = DocumentBuilderFactory.newInstance()
  .newDocumentBuilder().newDocument();
new Xembler(
  new Directives(
    "ADD 'orders'; ADD 'order'; ATTR 'id', '553'; SET '$140.00';"
  )
).apply(document);

Since version 0.9 you can directly transform directives to XML:

String xml = new Xembler(
  new Directives()
    .add("root")
    .add("order")
    .attr("id", "553")
    .set("$140.00")
).xml();

This code will produce the following XML document:

<root>
  <order id="553">$140</order>
</root>

Directives

This is a full list of supported directives, in the current version:

  • ADD: adds new node to all current nodes
  • ADDIF: adds new node, if it's absent
  • ATTR: sets new attribute to current nodes
  • SET: sets text value of current node
  • XSET: sets text value, calculating it with XPath
  • XATTR: sets attribute value, calculating it with XPath
  • CDATA: same as SET, but makes CDATA
  • UP: moves cursor one node up
  • XPATH: moves cursor to the nodes found by XPath
  • REMOVE: removes all current nodes
  • STRICT: throws an exception if cursor is missing nodes
  • PI: adds processing instruction
  • PUSH: saves cursor in stack
  • POP: retrieves cursor from stack
  • NS: sets namespace of all current nodes
  • COMMENT: adds XML comment

The "cursor" or "current nodes" is where we're currently located in the XML document. When Xembly script starts, the cursor is empty: it simply points to the highest level in the XML hierarchy. Pay attention, it doesn't point to the root node. It points to one level above the root. Remember, when a document is empty, there is no root node.

Then, we start executing directives one by one. After each directive the cursor is moving somewhere. There may be many nodes under the cursor, or just one, or none. For example, let's assume we're starting with this simple document <car/>:

ADD 'hello';        // Nothing happens, since the cursor is empty
XPATH '/car';       // There is one node <car> under the cursor
ADD 'make';         // The result is "<car><make/></car>",
                    // the cursor has one node "<make/>"
ATTR 'name', 'BMW'; // The result is "<car><make name='BMW'/></car>",
                    // the cursor still points to one node "<make/>"
UP;                 // The cursor has one node "<car>"
ADD 'mileage';      // The result is "<car><make name='BMW'/><mileage/></car>",
                    // the cursor still has one node "<car>"
XPATH '*';          // The cursor has two nodes "<make name='BMW'/>"
                    // and "<mileage/>"
REMOVE;             // The result is "<car/>", since all nodes under
                    // the cursor are removed

You can create a collection of directives either from a text or via supplementary methods, one per each directive. In both cases, you need to use the Directives class:

import org.xembly.Directives;
new Directives("XPATH '//car'; REMOVE;");
new Directives().xpath("//car").remove();

The second option is preferable, because it is faster — there is no parsing involved.

ADD

The ADD directive adds a new node to every node in the current node set. ADD expects exactly one mandatory argument, which is the name of a new node to be added (case sensitive):

ADD 'orders';
ADD 'order';

Even if a node with the same name already exists, a new node will be added. Use ADDIF if you need to add only if the same-name node is absent.

After the execution, the ADD directive moves the cursor to the nodes just added.

ADDIF

The ADDIF directive adds a new node to every node of the current set, only if it is absent. ADDIF expects exactly one argument, which is the name of the node to be added (case sensitive):

ADD 'orders';
ADDIF 'order';

After the execution, the ADDIF directive moves the cursor to the nodes just added.

ATTR

The ATTR directive sets an attribute to every node of the current set. ATTR expects exactly two arguments, where the first is the name of the attribute and the second is the value to set:

ADD 'order';
ATTR 'price', '$49.99';

After the execution, ATTR doesn't move the cursor.

If it's necessary to make sure the attribute belongs to a certain namespace, put the namespace and its prefix into the attribute name separating them with spaces:

ADD 'flower';
ATTR 'noNamespaceSchemaLocation xsi
  http://www.w3.org/2001/XMLSchema-instance', 'foo.xsd';

This will generate the following document:

<flower xsi:noNamespaceSchemaLocation="foo.xsd"/>

SET

The SET directive changes text content of all current nodes, and expects exactly one argument, which is the text content to set:

ADD "employee";
SET "John Smith";

SET doesn't move the cursor anywhere.

XSET

The XSET directive changes text content of all current nodes to a value calculated with the provided XPath expression:

ADD "product-1";
ADD "price";
XSET "sum(/products/price) div count(/products)";

XSET doesn't move the cursor anywhere.

XATTR

The XATTR directive changes the value of an attribute of all current nodes to a value calculated with the provided XPath expression:

ADD "product-1";
ADD "price";
XATTR "s", "sum(/products/price) div count(/products)";

XATTR doesn't move the cursor anywhere.

UP

The UP directive moves all current nodes to their parents.

XPATH

The XPATH directive re-points the cursor to the nodes found by the provided XPath expression:

XPATH "//employee[@id='234' and name='John Smith']/name";
SET "John R. Smith";

REMOVE

The REMOVE directive removes current nodes under the cursor and moves the cursor to their parents:

ADD "employee";
REMOVE;

STRICT

The STRICT directive checks that there is a certain number of current nodes:

XPATH "//employee[name='John Doe']";  // Move the cursor to the employee
STRICT "1";                           // Throw an exception if there
                                      // is not exactly one node under
                                      // the cursor

This is a very effective mechanism of validation of your script, in production mode. It is similar to assert statement in Java. It is recommended to use STRICT regularly, to make sure your cursor has correct amount of nodes, to avoid unexpected modifications.

STRICT doesn't move the cursor anywhere.

PI

The PI directive adds a new processing directive to the XML:

PI "xsl-stylesheet" "href='http://example.com'";

PI doesn't move the cursor anywhere.

PUSH and POP

The PUSH and POP directives save current DOM position to stack and restore it from there.

Let's say, you start your Xembly manipulations from a place in DOM, which location is not determined for you. After your manipulations are done, you want to get back to exactly the same place. You should use PUSH to save your current location and POP to restore it back, when manipulations are finished, for example:

PUSH;                        // Doesn't matter where we are
                             // 

Related Skills

View on GitHub
GitHub Stars246
CategoryDevelopment
Updated1mo ago
Forks30

Languages

Java

Security Score

85/100

Audited on Feb 11, 2026

No findings