Jackson xml десериализовать встроенный массив

Как десериализовать такой странный XML. По моему мнению, сущность props отсутствует (вокруг props), но я не могу изменить источник этого XML (веб-службы).

<parents>
  <parent name="first">
    <description><![CDATA[Description for the first-Entity]]></description>
    <prop name="level">
      <value><![CDATA[1]]></value>
    </prop>
    <prop name="enabled">
      <value><![CDATA[true]]></value>
    </prop>
    <prop name="version">
      <value><![CDATA[1.0-beta3]]></value>
    </prop>
  </parent>
  <parent name="second">...</parent>
  ...
</parents>

Мои сущности

public class Test {
    @Test
    public void deserializerTest() throws JsonParseException, JsonMappingException, IOException {
        ObjectMapper om = new XmlMapper();
        List<Parent> xml = om.readValue(new File("./test.xml"),
            new TypeReference<List<Parent>>() {});
    }
}

public class Prop {
    @JacksonXmlProperty(isAttribute = true)
    public String name;

    @JacksonXmlText
    public String value;
}

@JacksonXmlRootElement
public class Parent {
    @JacksonXmlProperty(isAttribute = true)
    public String name;

    public String description;

    // 1. alternative with List
    public List<Prop> prop;

    // 2. alternative with Map
    @JsonDeserialize(using = PropDeser.class)
    public Map<String, String> prop;
} 


public static class PropDeser extends JsonDeserializer<Map<String, String>> {

    @Override
    public Map<String, String> deserialize(JsonParser jp,
            DeserializationContext ctxt) throws IOException,
            JsonProcessingException {
        Map<String, String> ret = new HashMap<String, String>();
        boolean eof = false;
        while (jp.hasCurrentToken()) {
            JsonToken t = jp.getCurrentToken();
            switch (t) {
            case END_OBJECT:
                if (eof) {
                    return ret;
                }
                eof = true;
                break;
            case VALUE_STRING:
                ret.put(jp.getCurrentName(), jp.getText());
                break;
            default:
                eof = false;
                break;
            }
            jp.nextValue();
        }
        return null;
    }

}

1. Альтернатива

создает исключение: "Не удается создать экземпляр значения типа [простой тип, класс my.test.Prop] из JSON String; нет однострокового конструктора/фабричного метода (через цепочку ссылок: my.test.Parent ["prop"]) '

Я не хочу простой список строк. Мне нужны оба: имя и значение. Итак, я пришел к мысли использовать Map<String, String>, создав собственный десериализатор...

2. Альтернатива

Похоже, ошибка в том, что метод PropDeser.deserialize() потребляет закрывающий тег родительского элемента.

java.lang.NullPointerException
at com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap.find(BeanPropertyMap.java:160)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:287)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:112)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:226)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:203)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:23)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2575)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1766)
at my.test.Test.deserializerTest(Test.java:57)

Есть ли возможность итерировать в обратном направлении в XML-потоке? Как метод может знать, когда остановиться? Я понятия не имею.

Ответ 1

Должно быть возможно обработать "развернутый" стиль элементов списка с помощью модуля XML Jackson 2.1, с @JacksonXmlElementWrapper(useWrapping=false).

Структура должна быть примерно такой:

@JacksonXmlRootElement(localName="parents")
public class Parents {
  @JacksonXmlElementWrapper(useWrapping=false)
  public List<Parent> parent;
}

public class Parent {
  @JacksonXmlProperty(isAttribute=true)
  public String name;

  public String description;

  @JacksonXmlElementWrapper(useWrapping=false)
  public List<Prop> prop;
}

public class Prop {
  @JacksonXmlProperty(isAttribute=true)
  public String name;

  public String value;
}

поэтому ваше решение было довольно близко.

Обратите внимание, что если используются внутренние классы, они должны иметь "статическую" в декларации. Я тестировал это с помощью 2.1.4, и он работает для меня.