【问题标题】:Deserialize a Map from JAXB generated XML using Jackson使用 Jackson 从 JAXB 生成的 XML 反序列化 Map
【发布时间】:2019-11-20 21:45:32
【问题描述】:

我需要反序列化一些使用 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;
    }
}

请建议一种使用 Jackson 成功解析该地图的方法。

【问题讨论】:

    标签: java xml jackson jaxb


    【解决方案1】:

    默认Map被序列化为XML in:

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

    格式。没有entry 元素。你有两个选择:

    1. 更改模型,而不是 Map 使用 List&lt;Entry&gt; 类型。
    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}}
    

    【讨论】:

      猜你喜欢
      • 2012-12-07
      • 2015-04-06
      • 2017-01-11
      • 2013-12-05
      • 1970-01-01
      • 2017-01-23
      • 2017-03-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多