Sunday, July 06, 2014

Modern XML to Java

How many frameworks are there for converting XML to Java? Hard to count. As an experiment I tried my hand at one. I have two top-level classes plus an annotation:

public final class XMLFuzzy
        implements InvocationHandler {
    private static final XPath xpath = XPathFactory.newInstance().
    private static final Map<Method, XPathExpression> expressions
     = new ConcurrentHashMap<>();

    private final Node node;
    private final Converter converter;

    public static final class Factory {
        private final Converter converter;

        public Factory(final Converter converter) {
            this.converter = converter;

        public <T> T of(@Nonnull final Class<T> itf,
                @Nonnull final Node node) {
            return XMLFuzzy.of(itf, node, converter);

    public static <T> T of(@Nonnull final Class<T> itf,
     @Nonnull final Node node,
            @Nonnull final Converter converter) {
        return itf.cast(newProxyInstance(itf.getClassLoader(),
                new Class[]{itf},
                new XMLFuzzy(node, converter)));

    private XMLFuzzy(final Node node, final Converter converter) {
        this.node = node;
        this.converter = converter;

    public Object invoke(final Object proxy, final Method method,
         final Object[] args)
            throws Throwable {
        return converter.convert(method.getReturnType(), expressions.
                computeIfAbsent(method, XMLFuzzy::compile).

    private static XPathExpression compile(@Nonnull final Method method) {
        final String expression = asList(method.getAnnotations()).stream().
                orElseThrow(() -> new MissingAnnotation(method)).
        try {
            return xpath.compile(expression);
        } catch (final XPathExpressionException e) {
            throw new BadXPath(method, expression, e);

    public static final class MissingAnnotation
            extends RuntimeException {
        private MissingAnnotation(final Method method) {
            super(format("Missing @X(xpath) annotation: %s", method));

    public static final class BadXPath
            extends RuntimeException {
        private BadXPath(final Method method, final String expression,
                final XPathExpressionException e) {
            super(format("Bad @X(xpath) annotation on '%s': %s: %s",
                    method, expression, e.getMessage()));

I have left out Converter; it turns strings into objects of a given type, another example of overimplemented framework code in Java. And the annotation:

public @interface From {
    String value();

The idea is straight-forward: drive the object mapping from XML with XPaths. Credit to XMLBeam for introducing to me the elegant use of JDK proxies for this purpose.

Of course tests:

public final class XMLFuzzyTest {
    private Top top;

    public void setUp()
            throws ParserConfigurationException, IOException, SAXException {
        final Document document = DocumentBuilderFactory.newInstance().
                parse(new InputSource(new StringReader(XML)));
        top = new XMLFuzzy.Factory(new Converter()).of(Top.class, document);

    public void shouldHandleString() {
        assertThat(top.a(), is(equalTo("apple")));

    public void shouldHandlePrimitiveInt() {
        assertThat(top.b(), is(equalTo(3)));

    public void shouldHandleRURI() {
        assertThat(top.c(), is(equalTo(URI.create("http://some/where"))));

    @Test(expected = MissingAnnotation.class)
    public void shouldThrowOnMissingAnnotation() {

    @Test(expected = BadXPath.class)
    public void shouldThrowOnBadXPath() {

    public interface Top {
        // For the purposes of this blog post, pretend Java supports
 // multiline string literals
        String XML = "<top>

        String a();

        int b();

        URI c();

        void d();

        @From("dis' ain't xpath")
        void e();
Post a Comment