使用Jackson从JAXB生成的XML中反序列化Map

时间:2019-07-11 08:21:41

标签: java xml jackson jaxb

我需要反序列化使用JAXB生成的一些XML。 由于某些合规性问题,我只需要使用Jackson进行XML解析。 尝试反序列化具有Map的类时遇到异常

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
 at [Source: (StringReader); line: 40, column: 21] (through reference chain: FileConfig["otherConfigs"]->java.util.LinkedHashMap["entry"])

我的代码如下:

XML文件:

。 。

    <fileConfig>
        <whetherNotify>false</whetherNotify>
        <url>....some location....</url>
        <includes>app.log</includes>
        <fileType>...some string...</fileType>
        <otherConfigs>
            <entry>
                <key>pathType</key>
                <value>1</value>
            </entry>
        </otherConfigs>
    </fileConfig>

。 。

FileConfig.java

    public class FileConfig implements Serializable {

    protected Boolean whetherNotify = false;
    protected String url;
    protected String includes;
    protected FileType fileType;
    private Map<String, String> otherConfigs = new HashMap<String, String>();

    ....getters and setters.....
}

Main.java

public class Main {
  .
  .
  .
  .
   private static <T> T unmarshallFromXML(String xml, Class<T> parseToClass) throws IOException {
        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        xmlMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
        xmlMapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
        xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        xmlMapper.setDefaultUseWrapper(false);
        xmlMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        T parsedObject = xmlMapper.readValue(xml, parseToClass);
        return parsedObject;
    }
}

请提出一种使用杰克逊成功解析该地图的方法。

1 个答案:

答案 0 :(得分:0)

默认情况下,Map在以下位置被序列化为XML

...
<key>value</key>
<key1>value1</key1>
...

格式。没有entry元素。您有两种选择:

  1. 更改型号,而不是Map使用List<Entry>类型。
  2. Map类型实现自定义反序列化器。

新型号

您需要创建Entry类:

class Entry {
    private String key;
    private String value;

    // getters, setters, toString
}

并将FileConfig类中的属性更改为:

List<Entry> otherConfigs;

自定义地图解串器

请参见以下示例:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class XmlApp {

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


    XmlMapper xmlMapper = new XmlMapper();

    FileConfig fileConfig = xmlMapper.readValue(xmlFile, FileConfig.class);
    System.out.println(fileConfig);
  }
}

class MapEntryDeserializer extends JsonDeserializer<Map<String, String>> {

    @Override
    public Map<String, String> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        Map<String, String> map = new HashMap<>();

        JsonToken token;
        while ((token = p.nextToken()) != null) {
            if (token == JsonToken.FIELD_NAME) {
                if (p.getCurrentName().equals("entry")) {
                    p.nextToken();
                    JsonNode node = p.readValueAsTree();
                    map.put(node.get("key").asText(), node.get("value").asText());
                }
            }
        }
        return map;
    }
}

class FileConfig {

  protected Boolean whetherNotify = false;
  protected String url;
  protected String includes;

  @JsonDeserialize(using = MapEntryDeserializer.class)
  private Map<String, String> otherConfigs;

  // getters, setters, toString
}

上面的代码显示:

FileConfig{whetherNotify=false, url='....some location....', includes='app.log', otherConfigs={pathType1=2, pathType=1}}