Xembly
Assembly for XML: an imperative language for creating and modifying XML documents (and a Java library)
Install / Use
/learn @yegor256/XemblyREADME
XML Modifying Imperative Language (and Java Lib)
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 nodesADDIF: adds new node, if it's absentATTR: sets new attribute to current nodesSET: sets text value of current nodeXSET: sets text value, calculating it with XPathXATTR: sets attribute value, calculating it with XPathCDATA: same asSET, but makesCDATAUP: moves cursor one node upXPATH: moves cursor to the nodes found by XPathREMOVE: removes all current nodesSTRICT: throws an exception if cursor is missing nodesPI: adds processing instructionPUSH: saves cursor in stackPOP: retrieves cursor from stackNS: sets namespace of all current nodesCOMMENT: 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
node-connect
343.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
92.1kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
343.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
343.3kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
