使用JAXB解封/编列一个列表。

时间:2021-05-18 19:35:27

I'm trying to create a very simple REST server. I just have a test method that will return a List of Strings. Here's the code:

我正在尝试创建一个非常简单的REST服务器。我有一个测试方法,它将返回字符串列表。这是代码:


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

It gives the following error:

它给出了以下错误:

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

I was hoping JAXB had a default setting for simple types like String, Integer, etc. I guess not. Here's what I imagined:

我希望JAXB对简单类型(如String、Integer等)有一个默认设置。这就是我想象的:


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

What's the easiest way to make this method work?

使这种方法有效的最简单的方法是什么?

11 个解决方案

#1


46  

I used @LiorH's example and expanded it to:

我用@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;
    }
}

Note, that it uses generics so you can use it with other classes than String. Now, the application code is simply:

注意,它使用泛型,所以您可以使用它与其他类而不是字符串。现在,应用程序代码很简单:


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

Why doesn't this simple class exist in the JAXB package? Anyone see anything like it elsewhere?

为什么这个简单的类不存在于JAXB包中?有人看到过类似的东西吗?

#2


31  

@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();
}

#3


12  

In case anyone of you wants to write a list wrapper for lists containing elements of multiple classes and want to give an individual XmlElement name according to the Class type without Writing X Wrapper classes you could use the @XmlMixed annotation. By doing so JAXB names the items of the list according to the value set by the @XmlRootElement. When doing so you have to specify which classes could possibly be in the list using @XmlSeeAlso

如果您想为包含多个类元素的列表编写一个列表包装器,并希望根据类类型给出一个单独的XmlElement名称,而无需编写X包装类,您可以使用@XmlMixed注释。通过这样做,JAXB根据@XmlRootElement设置的值对列表项进行命名。当这样做时,您必须指定哪些类可以使用@XmlSeeAlso在列表中。

Example:

例子:

Possible Classes in the list

列表中可能的类。

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

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

Wrapper class

包装器类

@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;
    }
}

Example:

例子:

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


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

Result:

结果:

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

Alternatevily you could specify the XmlElement names directly inside the wrapper class using the @XmlElementRef annotation

您可以使用@XmlElementRef注释在包装器类中直接指定XmlElement名称。

@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;
    }
}

#4


11  

This can be done MUCH easier using wonderful XStream library. No wrappers, no annotations.

使用奇妙的XStream库可以更轻松地完成这一任务。没有包装,没有注释。

Target XML

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

Serialization

(String alias can be avoided by using lowercase string tag, but I used OP's code)

(使用小写字符串标记可以避免字符串别名,但我使用了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);

Deserialization

Deserialization into ArrayList

反序列化为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);

Deserialization into String[]

反序列化成String[]

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

Note, that XStream instance is thread-safe and can be pre-configured, shrinking code amount to one-liners.

注意,XStream实例是线程安全的,可以预先配置,减少代码量给一行程序。

XStream can also be used as a default serialization mechanism for JAX-RS service. Example of plugging XStream in Jersey can be found here

XStream还可以用作JAX-RS服务的默认序列化机制。可以在这里找到在Jersey中插入XStream的例子。

#5


9  

From a personal blog post, it is not necessary to create a specific JaxbList < T > object.

从个人博客帖子中,没有必要创建一个特定的JaxbList < T >对象。

Assuming an object with a list of strings:

假设一个带有字符串列表的对象:

@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;
    }

}

A JAXB round trip:

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(" ");

}

Produces the following:

产生以下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithList>
    <MyList>
        <list>Somewhere</list>
        <list>This and that</list>
        <list>Something</list>
    </MyList>
</objectWithList>

#6


8  

I have encountered this pattern a few times, I found that the easiest way is to define an inner class with JaxB annotations. (anyways, you'll probably want to define the root tag name)

我遇到过这种模式几次,我发现最简单的方法是用JaxB注释定义内部类。(无论如何,您可能需要定义根标记名)

so your code would look something like this

你的代码看起来是这样的。

@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);}
 }

if you work with javax.rs (jax-rs) I'd return Response object with the wrapper set as its entity

如果您使用javax。rs (jax-rs)返回响应对象,并将包装器设置为它的实体。

#7


2  

User1's example worked well for me. But, as a warning, it won't work with anything other than simple String/Integer types, unless you add an @XmlSeeAlso annotation:

User1的例子对我来说很有效。但是,作为一个警告,除了简单的字符串/整数类型之外,它不会使用任何其他类型,除非您添加一个@XmlSeeAlso注释:

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

This works OK, although it prevents me from using a single generic list class across my entire application. It might also explain why this seemingly obvious class doesn't exist in the JAXB package.

虽然这可以防止我在整个应用程序中使用单一的泛型列表类,但这是可行的。它还可以解释为什么JAXB包中不存在这个看起来很明显的类。

#8


2  

Finally I've solved it using JacksonJaxbJsonProvider It requires few changes in your Spring context.xml and Maven pom.xml

最后,我使用JacksonJaxbJsonProvider来解决它,它在您的Spring上下文中几乎不需要更改。xml和Maven pom.xml

In your Spring context.xml add JacksonJaxbJsonProvider to the <jaxrs:server>:

Spring上下文。xml将JacksonJaxbJsonProvider添加到 : 服务器>

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

In your Maven pom.xml add:

在您的Maven pom。xml添加:

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-jaxrs</artifactId>
    <version>1.9.0</version>
</dependency>

#9


0  

Make sure to add @XmlSeeAlso tag with your specific classes used inside JaxbList. It is very important else it throws HttpMessageNotWritableException

确保在JaxbList中添加了使用特定类的@XmlSeeAlso标记。它是非常重要的,它会抛出HttpMessageNotWritableException。

#10


0  

I would've saved time if I found Resteasy Jackson Provider sooner.

如果我能早点找到Resteasy Jackson供应商,我就能节省时间。

Just add the Resteasy Jackson Provider JAR. No entity wrappers. No XML annotations. No custom message body writers.

只需添加Resteasy Jackson Provider JAR。任何单位包装器。没有XML注释。没有自定义消息体写入器。

#11


0  

If you are using maven in the jersey project add below in pom.xml and update project dependencies so that Jaxb is able to detect model class and convert list to Media type application XML:

如果您在jersey项目中使用maven,请在pom中添加。xml和更新项目依赖关系,以便Jaxb能够检测模型类并将列表转换为媒体类型应用程序xml:

<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.2.11</version>
</dependency>

#1


46  

I used @LiorH's example and expanded it to:

我用@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;
    }
}

Note, that it uses generics so you can use it with other classes than String. Now, the application code is simply:

注意,它使用泛型,所以您可以使用它与其他类而不是字符串。现在,应用程序代码很简单:


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

Why doesn't this simple class exist in the JAXB package? Anyone see anything like it elsewhere?

为什么这个简单的类不存在于JAXB包中?有人看到过类似的东西吗?

#2


31  

@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();
}

#3


12  

In case anyone of you wants to write a list wrapper for lists containing elements of multiple classes and want to give an individual XmlElement name according to the Class type without Writing X Wrapper classes you could use the @XmlMixed annotation. By doing so JAXB names the items of the list according to the value set by the @XmlRootElement. When doing so you have to specify which classes could possibly be in the list using @XmlSeeAlso

如果您想为包含多个类元素的列表编写一个列表包装器,并希望根据类类型给出一个单独的XmlElement名称,而无需编写X包装类,您可以使用@XmlMixed注释。通过这样做,JAXB根据@XmlRootElement设置的值对列表项进行命名。当这样做时,您必须指定哪些类可以使用@XmlSeeAlso在列表中。

Example:

例子:

Possible Classes in the list

列表中可能的类。

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

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

Wrapper class

包装器类

@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;
    }
}

Example:

例子:

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


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

Result:

结果:

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

Alternatevily you could specify the XmlElement names directly inside the wrapper class using the @XmlElementRef annotation

您可以使用@XmlElementRef注释在包装器类中直接指定XmlElement名称。

@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;
    }
}

#4


11  

This can be done MUCH easier using wonderful XStream library. No wrappers, no annotations.

使用奇妙的XStream库可以更轻松地完成这一任务。没有包装,没有注释。

Target XML

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

Serialization

(String alias can be avoided by using lowercase string tag, but I used OP's code)

(使用小写字符串标记可以避免字符串别名,但我使用了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);

Deserialization

Deserialization into ArrayList

反序列化为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);

Deserialization into String[]

反序列化成String[]

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

Note, that XStream instance is thread-safe and can be pre-configured, shrinking code amount to one-liners.

注意,XStream实例是线程安全的,可以预先配置,减少代码量给一行程序。

XStream can also be used as a default serialization mechanism for JAX-RS service. Example of plugging XStream in Jersey can be found here

XStream还可以用作JAX-RS服务的默认序列化机制。可以在这里找到在Jersey中插入XStream的例子。

#5


9  

From a personal blog post, it is not necessary to create a specific JaxbList < T > object.

从个人博客帖子中,没有必要创建一个特定的JaxbList < T >对象。

Assuming an object with a list of strings:

假设一个带有字符串列表的对象:

@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;
    }

}

A JAXB round trip:

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(" ");

}

Produces the following:

产生以下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithList>
    <MyList>
        <list>Somewhere</list>
        <list>This and that</list>
        <list>Something</list>
    </MyList>
</objectWithList>

#6


8  

I have encountered this pattern a few times, I found that the easiest way is to define an inner class with JaxB annotations. (anyways, you'll probably want to define the root tag name)

我遇到过这种模式几次,我发现最简单的方法是用JaxB注释定义内部类。(无论如何,您可能需要定义根标记名)

so your code would look something like this

你的代码看起来是这样的。

@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);}
 }

if you work with javax.rs (jax-rs) I'd return Response object with the wrapper set as its entity

如果您使用javax。rs (jax-rs)返回响应对象,并将包装器设置为它的实体。

#7


2  

User1's example worked well for me. But, as a warning, it won't work with anything other than simple String/Integer types, unless you add an @XmlSeeAlso annotation:

User1的例子对我来说很有效。但是,作为一个警告,除了简单的字符串/整数类型之外,它不会使用任何其他类型,除非您添加一个@XmlSeeAlso注释:

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

This works OK, although it prevents me from using a single generic list class across my entire application. It might also explain why this seemingly obvious class doesn't exist in the JAXB package.

虽然这可以防止我在整个应用程序中使用单一的泛型列表类,但这是可行的。它还可以解释为什么JAXB包中不存在这个看起来很明显的类。

#8


2  

Finally I've solved it using JacksonJaxbJsonProvider It requires few changes in your Spring context.xml and Maven pom.xml

最后,我使用JacksonJaxbJsonProvider来解决它,它在您的Spring上下文中几乎不需要更改。xml和Maven pom.xml

In your Spring context.xml add JacksonJaxbJsonProvider to the <jaxrs:server>:

Spring上下文。xml将JacksonJaxbJsonProvider添加到 : 服务器>

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

In your Maven pom.xml add:

在您的Maven pom。xml添加:

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-jaxrs</artifactId>
    <version>1.9.0</version>
</dependency>

#9


0  

Make sure to add @XmlSeeAlso tag with your specific classes used inside JaxbList. It is very important else it throws HttpMessageNotWritableException

确保在JaxbList中添加了使用特定类的@XmlSeeAlso标记。它是非常重要的,它会抛出HttpMessageNotWritableException。

#10


0  

I would've saved time if I found Resteasy Jackson Provider sooner.

如果我能早点找到Resteasy Jackson供应商,我就能节省时间。

Just add the Resteasy Jackson Provider JAR. No entity wrappers. No XML annotations. No custom message body writers.

只需添加Resteasy Jackson Provider JAR。任何单位包装器。没有XML注释。没有自定义消息体写入器。

#11


0  

If you are using maven in the jersey project add below in pom.xml and update project dependencies so that Jaxb is able to detect model class and convert list to Media type application XML:

如果您在jersey项目中使用maven,请在pom中添加。xml和更新项目依赖关系,以便Jaxb能够检测模型类并将列表转换为媒体类型应用程序xml:

<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.2.11</version>
</dependency>