Working with XML is painful in Java, there are many options such as DOM, SAX, StAX, JAXB, XSLT but they’re all verbose compared to the equivalent code in Clojure where you can make use of Maps and Zippers.

In Clojure my approach is usually as follows:

Parse the XML into a map Navigate the map using a zipper Create a new map with the values we’re interested in Convert the new map to JSON This example follows the above process but calls the Clojure code from Java, ideally I’d wrap this up in a library but to keep things simple I’ve invoked the dependencies from Java prior to calling the transform library.

Source code on GitHub: https://github.com/alexanderjamesking/clj-from-java-xml-to-json

package com.ajk;

import clojure.lang.IFn;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import static clojure.java.api.Clojure.read;
import static clojure.lang.RT.loadResourceScript;
import static clojure.lang.RT.var;

public final class Transform {

    private final IFn xmlToJson;

    public Transform() throws IOException {
        // load libraries used by the transform script
        final IFn require = var("clojure.core", "require");
        require.invoke(read("clojure.xml"));
        require.invoke(read("clojure.zip"));
        require.invoke(read("clojure.data.json"));
        require.invoke(read("clojure.data.zip.xml"));

        loadResourceScript("com/ajk/transform.clj");
        xmlToJson = var("transform", "xml-to-json");
    }

    public String xmlToJson(final String xml) {
        return (String) xmlToJson.invoke(new ByteArrayInputStream(xml.getBytes()));
    }

}
(ns transform
  (:require [clojure.xml :refer [parse]]
            [clojure.zip :refer [xml-zip node up]]
            [clojure.data.zip.xml :refer [xml-> xml1-> text attr attr=]]
            [clojure.data.json :refer [write-str]]))

(defn- team-name [event alignment]
  (xml1-> event :team :team-metadata (attr= :alignment alignment) :name (attr :first)))

(defn- sports-event [event]
  {
    :date-time (xml1-> event :event-metadata (attr :start-date-time))
    :home-team (team-name event "home")
    :away-team (team-name event "away")
  })

(defn- tournament-round [round]
  {
    :name (xml1-> round :tournament-round-metadata (attr :round-name))
    :events (for [e (xml-> round :sports-event)]
              (sports-event e))
  })

(defn- tournament-stage [stage]
  {
    :name (xml1-> stage :tournament-stage-metadata (attr :stage-name))
    :rounds (for [r (xml-> stage :tournament-round)]
              (tournament-round r))
  })

(defn- xml-to-map [x]
  (let [m (xml1-> x :tournament :tournament-metadata)
        d (xml1-> x :tournament :tournament-division)
        knockout-stages (xml-> d :tournament-stage :tournament-stage)]
      {
        :title (xml1-> m (attr :tournament-name))
        :start-date-time (xml1-> m (attr :start-date-time))
        :end-date-time (xml1-> m (attr :end-date-time))
        :knockouts (for [s (xml-> d :tournament-stage :tournament-stage)]
                     (tournament-stage s))
      }))

(defn- map-to-json [m]
  (write-str m :escape-unicode false))

(defn xml-to-json [xml]
  (let [x (parse xml)
        x (xml-zip x)
        m (xml-to-map x)]
    (map-to-json m)))