Использование JAXB для демаршалирования / маршалирования списка <String> - PullRequest
45 голосов
/ 22 октября 2009

Я пытаюсь создать очень простой REST-сервер. У меня просто есть тестовый метод, который будет возвращать список строк. Вот код:


@GET
@Path("/test2")
public List test2(){
    List list=new Vector();
    list.add("a");
    list.add("b");
    return list;
}

выдает следующую ошибку:

SEVERE: A message body writer for Java type,
class java.util.Vector, and MIME media type,
application/octet-stream, was not found

Я надеялся, что JAXB имеет настройку по умолчанию для простых типов, таких как String, Integer и т.д. Вот что я себе представлял:


<Strings>
  <String>a</String>
  <String>b</String>
</Strings>

Какой самый простой способ заставить этот метод работать?

Ответы [ 12 ]

46 голосов
/ 22 октября 2009

Я использовал пример @ LiorH и расширил его до:


@XmlRootElement(name="List")
public class JaxbList<T>{
    protected List<T> list;

    public JaxbList(){}

    public JaxbList(List<T> list){
        this.list=list;
    }

    @XmlElement(name="Item")
    public List<T> getList(){
        return list;
    }
}

Обратите внимание, что он использует дженерики, поэтому вы можете использовать его с другими классами, кроме String. Теперь код приложения просто:


    @GET
    @Path("/test2")
    public JaxbList test2(){
        List list=new Vector();
        list.add("a");
        list.add("b");
        return new JaxbList(list);
    }

Почему этот простой класс не существует в пакете JAXB? Кто-нибудь видит что-нибудь подобное в другом месте?

32 голосов
/ 26 июля 2010
@GET
@Path("/test2")
public Response test2(){
   List<String> list=new Vector<String>();
   list.add("a");
   list.add("b");

   final GenericEntity<List<String>> entity = new GenericEntity<List<String>>(list) { };
   return Response.ok().entity(entity).build();
}
12 голосов
/ 06 марта 2012

В случае, если кто-либо из вас хочет написать оболочку списка для списков, содержащих элементы нескольких классов, и хотите дать отдельное имя XmlElement в соответствии с типом класса без написания классов X Wrapper, вы можете использовать аннотацию @XmlMixed. При этом JAXB называет элементы списка в соответствии со значением, установленным @XmlRootElement. При этом вы должны указать, какие классы могут быть в списке, используя @XmlSeeAlso

Пример:

Возможные классы в списке

@XmlRootElement(name="user")
public class User {/*...*/}

@XmlRootElement(name="entry")
public class LogEntry {/*...*/}

Класс обертки

@XmlRootElement(name="records")
@XmlSeeAlso({User.class, LogEntry.class})
public static class JaxbList<T>{

    protected List<T> records;

    public JaxbList(){}

    public JaxbList(List<T> list){
        this.records=list;
    }

    @XmlMixed 
    public List<T> getRecords(){
        return records;
    }
}

Пример:

List l = new List();
l.add(new User("userA"));
l.add(new LogEntry(new UserB()));


XStream xStream = new XStream();
String result = xStream.toXML(l);

Результат:

<records>
    <user>...</user>
    <entry>...</entry>
</records>

В качестве альтернативы вы можете указать имена XmlElement непосредственно внутри класса оболочки, используя аннотацию @XmlElementRef

@XmlRootElement(name="records")
@XmlSeeAlso({User.class, LogEntry.class})
public static class JaxbList<T>{

    protected List<T> records;

    public JaxbList(){}

    public JaxbList(List<T> list){
        this.records=list;
    }

    @XmlElementRefs({
        @XmlElementRef(name="item", type=Object.class),
        @XmlElementRef(name="user", type=User.class),
        @XmlElementRef(name="entry", type=LogEntry.class)
    })
    public List<T> getRecords(){
        return records;
    }
}
11 голосов
/ 07 августа 2012

Из личного блога пост , нет необходимости создавать конкретный JaxbList < T > объект.

Предполагается, что объект со списком строк:

@XmlRootElement
public class ObjectWithList {

    private List<String> list;

    @XmlElementWrapper(name="MyList")
    @XmlElement
    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

}

JAXB туда и обратно:

public static void simpleExample() throws JAXBException {

    List<String> l = new ArrayList<String>();
    l.add("Somewhere");
    l.add("This and that");
    l.add("Something");

    // Object with list
    ObjectWithList owl = new ObjectWithList();
    owl.setList(l);

    JAXBContext jc = JAXBContext.newInstance(ObjectWithList.class);
    ObjectWithList retr = marshallUnmarshall(owl, jc);

    for (String s : retr.getList()) {
        System.out.println(s);
    } System.out.println(" ");

}

Производит следующее:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithList>
    <MyList>
        <list>Somewhere</list>
        <list>This and that</list>
        <list>Something</list>
    </MyList>
</objectWithList>
10 голосов
/ 08 декабря 2011

Это можно сделать НАМНОГО проще, используя замечательную библиотеку XStream . Без оберток, без аннотаций.

Целевой XML

<Strings>
  <String>a</String>
  <String>b</String>
</Strings>

Сериализация

(псевдоним String можно избежать с помощью тега string в нижнем регистре, но я использовал код OP)

List <String> list = new ArrayList <String>();
list.add("a");
list.add("b");

XStream xStream = new XStream();
xStream.alias("Strings", List.class);
xStream.alias("String", String.class);
String result = xStream.toXML(list);

Десериализация

десериализация в ArrayList

XStream xStream = new XStream();
xStream.alias("Strings", ArrayList.class);
xStream.alias("String", String.class);
xStream.addImplicitArray(ArrayList.class, "elementData");
List <String> result = (List <String>)xStream.fromXML(file);

десериализация в строку []

XStream xStream = new XStream();
xStream.alias("Strings", String[].class);
xStream.alias("String", String.class);
String[] result = (String[])xStream.fromXML(file);

Обратите внимание, что экземпляр XStream является поточно-ориентированным и может быть предварительно сконфигурирован, сокращая количество кода до однострочников.

XStream также может использоваться в качестве механизма сериализации по умолчанию для службы JAX-RS. Пример подключения XStream в Джерси можно найти здесь

8 голосов
/ 22 октября 2009

Я встречался с этим шаблоном несколько раз и обнаружил, что самый простой способ - определить внутренний класс с аннотациями JaxB. (в любом случае, вы, вероятно, захотите определить имя корневого тега)

чтобы ваш код выглядел примерно так

@GET
@Path("/test2")
public Object test2(){
   MyResourceWrapper wrapper = new MyResourceWrapper();
   wrapper .add("a");
   wrapper .add("b");
   return wrapper ;
}

@XmlRootElement(name="MyResource")
private static class MyResourceWrapper {
       @XmlElement(name="Item")
       List<String> list=new ArrayList<String>();
       MyResourceWrapper (){}

       public void add(String s){ list.add(s);}
 }

если вы работаете с javax.rs (jax-rs), я бы возвратил объект Response с оберткой, установленной в качестве его сущности

3 голосов
/ 11 июня 2015

Наконец-то я решил это с помощью JacksonJaxbJsonProvider Требуется несколько изменений в вашей Spring context.xml и Maven pom.xml

Весной context.xml добавьте JacksonJaxbJsonProvider к <jaxrs:server>:

<jaxrs:server id="restService" address="/resource">
    <jaxrs:providers>
        <bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
    </jaxrs:providers>
</jaxrs:server>

В вашем Maven pom.xml добавьте:

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-jaxrs</artifactId>
    <version>1.9.0</version>
</dependency>
2 голосов
/ 10 марта 2010

Пример User1 работал хорошо для меня. Но, как предупреждение, он не будет работать ни с чем, кроме простых типов String / Integer, если вы не добавите аннотацию @XmlSeeAlso:

@XmlRootElement(name = "List")
@XmlSeeAlso(MovieTicket.class)
public class MovieTicketList {
    protected List<MovieTicket> list;

Это работает нормально, хотя и не позволяет мне использовать один общий класс списка для всего моего приложения. Это также может объяснить, почему этот, казалось бы, очевидный класс не существует в пакете JAXB.

0 голосов
/ 17 мая 2018

Для более общего решения, для сериализации JAXB-XML любого списка верхнего уровня, для которого требуется написать только 1 новый класс, проверьте решение, приведенное в этом вопросе:

Можно ли программно настроить JAXB?

public class Wrapper<T> {

private List<T> items = new ArrayList<T>();

@XmlAnyElement(lax=true)
public List<T> getItems() {
    return items;
}

}

//JAXBContext is thread safe and so create it in constructor or 
//setter or wherever:
... 
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz);
... 

public String marshal(List<T> things, Class clazz) {

  //configure JAXB and marshaller     
  Marshaller m = jc.createMarshaller();
  m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

  //Create wrapper based on generic list of objects
  Wrapper<T> wrapper = new Wrapper<T>(things);
  JAXBElement<Wrapper> wrapperJAXBElement = new JAXBElement<Wrapper>(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper);

  StringWriter result = new StringWriter();
  //marshal!
  m.marshal(wrapperJAXBElement, result);

  return result.toString();

}
0 голосов
/ 23 января 2017

Если вы используете maven в проекте jersey, добавьте ниже в pom.xml и обновите зависимости проекта, чтобы Jaxb мог определить класс модели и преобразовать список в XML-файл приложения Media type:

<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.2.11</version>
</dependency>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...