Search code examples
javaxmljacksonhashmapjackson-dataformat-xml

Coverting a Java Map to XML using Jackson library


How do I convert a Java Map to XML using Jackson?

Version:

'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8'

Code:

import com.fasterxml.jackson.dataformat.xml.XmlMapper;

public class MainApp {
    
    public static void main(String[] args) throws JsonProcessingException {

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("key1", "value1");
        map.put("key2", "value2");

        Application app = new Application();
        app.setEntry(map);

        // xml output format
        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true);
        System.out.println(xmlMapper.writerWithDefaultPrettyPrinter().writeValueAsString(app));

    }
    
    @JacksonXmlRootElement(localName = "headers")
    public static class Application {

        private Map<String, Object> entry;

        public Map<String, Object> getEntry() {
            return Collections.unmodifiableMap(entry);
        }

        public void setEntry(Map<String, Object> entry) {
            this.entry = entry;
        }
    }   
    
}

Actual output:

<?xml version='1.0' encoding='UTF-8'?>
<headers>
  <entry>
    <key1>value1</key1>
    <key2>value2</key2>
  </entry>
</headers>

Desired output:

<?xml version='1.0' encoding='UTF-8'?>
<headers>
    <entry key="key1">value1</entry>
    <entry key="key2">value2</entry>
</headers>

Solution

  • You need to implement custom serialiser to write Map entry like this. Example implementation:

    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    import com.fasterxml.jackson.dataformat.xml.XmlMapper;
    import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
    import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
    
    import java.io.IOException;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    public class XmlApp {
        public static void main(String[] args) throws IOException {
            Map<String, Object> map = new HashMap<>();
            map.put("key1", "value1");
            map.put("key2", "value2");
    
            Application app = new Application();
            app.setEntry(map);
    
            // xml output format
            XmlMapper xmlMapper = new XmlMapper();
            xmlMapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true);
            System.out.println(xmlMapper.writerWithDefaultPrettyPrinter().writeValueAsString(app));
        }
    }
    
    @JacksonXmlRootElement(localName = "headers")
    @JsonSerialize(using = ApplicationJsonSerializer.class)
    class Application {
    
        private Map<String, Object> entry;
    
        public Map<String, Object> getEntry() {
            return Collections.unmodifiableMap(entry);
        }
    
        public void setEntry(Map<String, Object> entry) {
            this.entry = entry;
        }
    }
    
    class ApplicationJsonSerializer extends JsonSerializer<Application> {
    
        @Override
        public void serialize(Application value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            ToXmlGenerator xmlGen = (ToXmlGenerator) gen;
            xmlGen.writeStartObject();
            for (Map.Entry<String, Object> entry : value.getEntry().entrySet()) {
                xmlGen.writeObjectFieldStart("entry");
                writeAttributes(xmlGen, entry.getKey());
                xmlGen.writeRaw(entry.getValue().toString());
                xmlGen.writeEndObject();
            }
            xmlGen.writeEndObject();
        }
    
        private void writeAttributes(ToXmlGenerator gen, String key) throws IOException {
            gen.setNextIsAttribute(true);
            gen.writeFieldName("key");
            gen.writeString(key);
            gen.setNextIsAttribute(false);
        }
    }
    

    Above code prints:

    <?xml version='1.0' encoding='UTF-8'?>
    <headers>
      <entry key="key1">value1</entry>
      <entry key="key2">value2</entry>
    </headers>