Десериализовать карту из сгенерированного JAXB XML, используя Джексона - PullRequest
1 голос
/ 11 июля 2019

Мне нужно десериализовать некоторые XML-файлы, которые генерируются с использованием JAXB.Из-за некоторых проблем с соответствием, я должен использовать Джексона только для разбора 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 голосов
/ 11 июля 2019

По умолчанию Map сериализуется в XML in:

...
<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}}
...