Как мне настроить Джексона для создания XML с использованием @XmlRootElement в элементах списка

У меня есть Spring-MVC @RestController, который использует общие имена, а не имена, которые я настроил с помощью @XmlRootElement или @JacksonXmlRootElement. Я хочу, чтобы XML выглядел так:

<list>
  <foo>
    <name>John</name>
  </foo>
</list>

но получаю следующее:

<ArrayList>
  <item>
    <name>John</name>
  </item>
</ArrayList>

Маршаллинг одного экземпляра правильно выглядит так:

<foo>
  <name>John</name>
</foo>

Чтобы решить эту проблему, я попытался использовать аннотации как Jackson, так и JAXB. Я также провел обширный поиск чужого решения в Stack Overflow, различных блогах и проблемах, связанных с Jackson и Spring-mvc.

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.junit.Test;    
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;

public class JacksonXmlTest {

    @XmlRootElement(name="foo")
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Foo {
        private String name;

        public Foo(String name) {
            setName(name);
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    @Test
    public void understandListTest() throws JsonProcessingException {
        // This is a JUnit test.....
        List<Foo> fooList = new ArrayList<>();
        fooList.add(new Foo("John"));

        XmlMapper mapper = new XmlMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.registerModule(new JaxbAnnotationModule());
        System.err.println(mapper.writeValueAsString(fooList));
        System.err.println();
        System.err.println(mapper.writeValueAsString(fooList.get(0)));
    }

}

Пожалуйста, помогите мне настроить Джексона для вывода списка, заключенного в теги «list», и чтобы каждый объект Foo содержался в тегах «foo», а не в тегах «item».


person Tim Perry    schedule 21.08.2019    source источник


Ответы (2)


Вы должны создать класс ListFoo, содержащий список объектов Foo:

@XmlRootElement(name="list")
@XmlAccessorType(XmlAccessType.FIELD)
public class ListFoo {

    @XmlElement(name = "foo")
    private List<Foo> listFoo;

    // getters & setters

 }
person tcharaf    schedule 21.08.2019
comment
Да, это сработает. Однако затем в моем контроллере Spring я застрял в реализации отдельных методов для JSON и XML, потому что мне нужно создать этот класс-оболочку для XML. Это так хорошо, как есть? - person Tim Perry; 22.08.2019

Используя функцию MixIn, вы можете определить имя для класса ArrayList и других из java.util.*.

import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.addMixIn(ArrayList.class, ArrayListMixIn.class);
        xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);

        List<Item> items = new ArrayList<>();
        items.add(new Item());
        items.add(new Item());
        System.out.println(xmlMapper.writeValueAsString(items));
    }
}

@JacksonXmlRootElement(localName = "list")
interface ArrayListMixIn {
}

class Item {

    private String name = "random - " + ThreadLocalRandom.current().nextDouble();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Над кодом печатается:

<list>
  <item>
    <name>random - 0.2256442724594785</name>
  </item>
  <item>
    <name>random - 0.9958813192003821</name>
  </item>
</list>
person Michał Ziober    schedule 21.08.2019
comment
Может ли Mix-In помочь с тегом ‹item›? - person Tim Perry; 22.08.2019
comment
@TimPerry, к сожалению, это жестко запрограммировано в XmlSerializerProvider. Взгляните на _startRootArray метод. Вы, конечно, можете попытаться переопределить это, но я не вижу простого способа добиться этого. Вероятно, самый простой способ - реализовать собственный сериализатор для списка, но он также выглядит сложным. - person Michał Ziober; 22.08.2019
comment
comment
Интересная ссылка. Придется подумать, какой из них будет более ремонтопригодным. Я склоняюсь к предложенной @tcharaf обертке. Спасибо за пищу для размышлений! - person Tim Perry; 22.08.2019