Java 使用 JAXB 解组/编组 List<String>

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1603404/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 16:39:01  来源:igfitidea点击:

Using JAXB to unmarshal/marshal a List<String>

javarestjaxb

提问by User1

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?

使这种方法起作用的最简单方法是什么?

采纳答案by User1

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:

请注意,它使用泛型,因此您可以将它与 String 以外的其他类一起使用。现在,应用程序代码很简单:


    @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 包中不存在这个简单的类?有人在其他地方看到过类似的东西吗?

回答by LiorH

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),我会返回 Response 对象,并将包装器设置为其实体

回答by piepera

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 注释,否则除了简单的 String/Integer 类型外,它不能用于任何其他类型:

@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 包中不存在这个看似明显的类。

回答by Sample Code

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

回答by Alex Abdugafarov

This can be done MUCHeasier using wonderful XStreamlibrary. No wrappers, no annotations.

这是可以做到MUCH更容易使用精彩的XStream库。没有包装器,没有注释。

Target XML

目标 XML

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

Serialization

序列化

(Stringalias can be avoided by using lowercase stringtag, but I used OP's code)

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

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 的示例

回答by Zounadire

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 @XmlMixedannotation. 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

如果您想为包含多个类的元素的列表编写一个列表包装器,并希望根据 Class 类型给出一个单独的 XmlElement 名称而不编写 X Wrapper 类,您可以使用@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 @XmlElementRefannotation

或者,您可以使用@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;
    }
}

回答by Jér?me Verstrynge

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>

回答by Maggy

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

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

回答by mstrthealias

I would've saved time if I found Resteasy Hymanson Providersooner.

如果我早点找到Resteasy Hymanson Provider,我会节省时间。

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

只需添加Resteasy Hymanson Provider JAR。没有实体包装器。没有 XML 注释。没有自定义消息正文作者。

回答by petrsyn

Finally I've solved it using HymansonJaxbJsonProviderIt requires few changes in your Spring context.xmland Maven pom.xml

最后我使用HymansonJaxbJsonProvider它解决了它需要在您的 Springcontext.xml和 Maven 中进行很少的更改pom.xml

In your Spring context.xmladd HymansonJaxbJsonProviderto the <jaxrs:server>:

在您的 Springcontext.xml添加HymansonJaxbJsonProvider<jaxrs:server>

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

In your Maven pom.xml add:

在您的 Maven pom.xml 添加:

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