Not a hitch in sight

A new approach to XML – Part 3: Seamless usage of namespaces and easy projector setup

SvenEwald
seamless

All you need to know for handling name spaces, configuring the data projector, and separating XPath expressions from the Java code.

In the third part of this series we are going to demonstrate
the proper way to handle name spaces, configure the data projector,
and separate XPath expressions from the Java code. If you want to
make the most of this tutorial, we also recommend reading ‘Part
1: Data projection with XMLBeam
’ and ‘Part
2: Features of the XMLBeam Library

The combination of XML-namespaces with XPath has
several stumbling blocks, and may well have caused you problems in
the past. In the document defined prefixes do not
automatically apply to XPath expressions. Whilst default
namespaces do apply to sub-elements, they do not apply to
attributes unlike expectations. According to the XPath 1.0
specification, an XPath selector without a prefix is always an
element without namespace – even though there is an element with a
default namespace.

To make matters worse, processing namespaces in
the
JDK’ s XML-parser is disabled
initially, and the XPath engine throws an exception on selecting
missing prefixes. To use namespaces in a flexible and comfortable
way XMLBeam offers three different approaches in terms of so-called
namespace-philosophies (see

DefaultXMLFactoriesConfig.NamespacePhilosophy).
A namespace philosophy is a set of defaults applied to the
underlaying XML-tools.

The first option is ignoring namespaces
completely, which comes closest to the JDK defaults. The XML parser
and the XPath evaluator just does not see any attribute or element
with a namespace. The used
DocumentBuilder
is configured to ignore namespaces via the method
“setNamespaceAware(false)“. Thus, the enum for this namespace
philosophy is called:
NIHILISTIC“.

The second option is delegating decisions about
namespace handling to the underlaying factory methods. The

DocumentBuilder, XPath
instance, and Transformer are used
without touching their configuration. The user himself can (and
must) decide on namespace handling and install a prefix mapping.
XMLBeam does not decide whether namespaces exist in the document
and therefore the enumeration for this philosophy is called
“AGNOSTIC”.

The third option is the default of the
class
XBProjector. XMLBeam takes existing
prefix mappings from the document and forwards them to the

XPathFactory. Nonexistent namespaces don’t break
the XPath evaluation anymore, but are treated as nonexistent
elements. Finally, elements with default namespace become
selectable by an automatically predefined prefix “xbdefaultns:”.
These defaults should ease most namespace related tasks, thus the
enum for this philosophy is called “HEDONISTIC”.

Choosing from namespace philosophies is one
purpose of the class

DefaultXMLFactoriesConfig. It represents the
default settings for the class

XBProjector. The API is designed to support
multiple implementations of the interface

XMLFactoriesCofig, which is used to control the
creation of
DocumentBuilder-, XPath-,
and Transformer
instances.

The class
DefaultXMLFactoriesConfig provides several
useful settings. It’s possible to choose whether the resulting XML
document is formatted to a readable representation (aka. “pretty
printing”) or whether the header contains an XML declaration (see
[2]).

Access to the current projector configuration is
done via calling projector.config(), which with a little syntax
sugar helps to avoid manual casting to the implementation
type.

Methods of the default configuration are
accessible directly via
projector.config().as(
DefaultXMLFactoriesConfig.class).*.
The cast is hidden in the fluent interface which saves some
brackets for readability. By the way, method chaining is supported
by all projections too. Setters and deleters are allowed to declare
the projection type as return type instead of

void. In this way these methods may be invoked
one after another directly.

The projector itself has two more flags to
control the behaviour of projections. The flag
SYNCHRONIZE_ON_DOCUMENTS” can be used to
make the projector create all projections in a synchronized
version. The synchronisation is done on the root of the DOM-tree.
This ensures thread-safety by preventing concurrency on all
projection methods in all projections and
sub-projections.

The second flag
TO_STRING_RENDERS_XML” changes the
rendering of the toString() method.

Sometimes it’s useful to make projections render
their XML content directly via toString(). This has to be enabled
explicitly, because large documents might have side effects when
debugging in the IDE.

The default implementation of the toString()
method renders a short, debug-friendly projection description. The
next part of this series is going to explain how to implement your
own version of the toString() method.

A further configuration option affects the
origin of the XPath expressions. In the previous examples, the
XPath expressions are located in the
@XBRead
and @XBWrite annotations beside the
projection methods. Via projector.config().setExternalizer(…) you
may choose a different way. The interface

Externalizer is responsible to assign a XPath
expression to a projection method. How this assignment is done, is
left to the user’s implementation. One example, the

PropertyFileExternalizer is included in the
library offering the
opportunity to store
the XPath expressions outside the Java code and even to change them
during runtime. One further option is to derive the expressions
from the projection methods names. This is demonstrated in the next
example.

Simple example structure for a projection
without XPath

<Department>
<Users>
<Name>John Doe</Name>
<Name>Tommy Atkins</Name>
</Users>
</Department>

Projection interface without declaring
XPath

@XBDocURL(“resource://“)
public interface ExampleProjection {
@XBRead
List<String> getDepartmentUsersName();
}

Example code with user defined
Externalizer

XBProjector projector = new XBProjector();
projector.config().setExternalizer(new ExternalizerAdapter() {
@Override
public String resolveXPath(String annotationValue, Method method, Object[] args) {
// Simplest conversion of camel case getter to XPath expression.
return method.getName().substring(3).replaceAll("[A-Z]","/$0");
}
});
List<Sting> departmentUsers = projector.io().fromURLAnnotation(ExampleProjection.class).getDepartmentUsersName();

In the fourth and last part of this series we
are going to address building complex XML-documents, explain how to
enrich projections with own method implementations, and demonstrate
how the type converter can be extended.

About the
author

Sven Ewald (Twitter handle:@Cfx) is the author of
the library XMLBeam (@XMLBeam). He has been creating Java solutions
for 15 years with no end in sight. Currently he works in the field
of domain-specific languages for the automotive industry.

Links & Literature

[1] http://xmlbeam.org

[2]

http://www.w3.org/TR/REC-xml/#NT-XMLDecl

 

 




Author
Comments
comments powered by Disqus