user292049
user292049

Reputation: 1138

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>

Upvotes: 5

Views: 6604

Answers (2)

user292049
user292049

Reputation: 1138

I found another solution

import org.apache.camel.Body;
import org.apache.camel.Exchange;
import org.apache.camel.Handler;
import org.apache.camel.Headers;

import lombok.Data;
import lombok.Setter;

....

    @Override
    @Handler
    public void record(Exchange exchange, @Headers Map<String, Object> headers, @Body String payload) {

     XmlMapper xmlMapper = new XmlMapper(); // XmlMapper is the main class from Jackson 2.x that helps us in serialization

        // Convert Camel headers HashMap to xml format
        String headersXml = "";
        try {
            MessageMapHolder messageMapHolder = new MessageMapHolder(headers);
            headersXml = xmlMapper.writeValueAsString(messageMapHolder);
        } catch (JsonProcessingException e) {
            log.error("Error during xml serialization", e);
        }
 ....
}
    /**
     * Serialise to write Map entry into the below structure
     * e.g.
     * <headers>
     * <entry key="key1">value1</entry>
     * <entry key="key2">value2</entry>
     * </headers>
     */
    @JacksonXmlRootElement(localName = "headers")
    @Data
    private static class MessageMapHolder {

        @Setter
        @JacksonXmlProperty(localName = "entry")
        private List<Entry> entries;

        MessageMapHolder(Map<String, Object> headers) {
            try {
                entries = new ArrayList<>();
                headers.forEach((k, v) -> {
                    entries.add(new Entry(k, String.valueOf(v)));
                });
            } catch (Exception e) {
                log.warn("Error in converting Camel Header to String: {}", e.getMessage());
            }
        }

        @Data
        @AllArgsConstructor
        private static class Entry {
            @JacksonXmlProperty(isAttribute = true)
            private String key;

            @JacksonXmlText
            private String value;
        }
    }

Upvotes: 0

Michał Ziober
Michał Ziober

Reputation: 38720

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>

Upvotes: 4

Related Questions