Deserialize a Map with custom entry names using Jackson XML

784 Views Asked by At

I have an XML document to deserialize with Jackson:

<root>
  <properties>
    <property>
      <key>k1</key>
      <value>v1<value>
    </property>
  </properties>
</root>

As you can see, /root/properties looks very much like a map, with each /root/properties/property resembling a Map.Entry.

Is there a way to create a POJO for deserializing this into a Map<String, String> without needing a custom deserializer?

I was hoping for something like the following, but it didn't work:

@JacksonXmlRootElement(localName = "root")
public class Root {
  @JacksonXmlElementWrapper(localName = "properties")
  public Map<String, String> properties;
}

The error I get from this is:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
1

There are 1 best solutions below

0
On

You can use JsonAnySetter annotation:

import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

import java.io.File;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class XmlMapperApp {

    public static void main(String... args) throws Exception {
        File xmlFile = new File("./resource/test.xml").getAbsoluteFile();

        XmlMapper mapper = XmlMapper.xmlBuilder().build();
        Root root = mapper.readValue(xmlFile, Root.class);
        System.out.println(root.getProperties());
    }
}

@JacksonXmlRootElement(localName = "root")
class Root {

    @JsonIgnore
    private Map<String, String> properties = new LinkedHashMap<>();

    @JsonAnySetter
    public void setAllProperties(String value, List<Map<String, String>> xmlProperties) {
        if (xmlProperties == null) {
            return;
        }
        this.properties = xmlProperties.stream().reduce(new LinkedHashMap<>(), (l, r) -> {
            l.put(r.get("key"), r.get("value"));
            return l;
        });
    }

    public Map<String, String> getProperties() {
        return properties;
    }
}

Above code prints for above XML:

{k1=v1}