代码之家  ›  专栏  ›  技术社区  ›  User1

使用JAXB解组/封送列表<string>

  •  45
  • User1  · 技术社区  · 15 年前

    我正在尝试创建一个非常简单的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对字符串、整数等简单类型有一个默认设置。我想不是。以下是我的想象:

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

    最简单的方法是什么?

    12 回复  |  直到 6 年前
        1
  •  46
  •   User1    15 年前

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

    注意,它使用泛型,这样您就可以将它与字符串以外的其他类一起使用。现在,应用程序代码只是:

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

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

        2
  •  31
  •   Sample Code    14 年前
    @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
  •   Zounadire    10 年前

    如果您中的任何人想要为包含多个类元素的列表编写一个列表包装器,并且想要根据类类型给出一个单独的xmlElement名称,而不需要编写x包装器类,您可以使用 @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>
    

    或者,可以使用 @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;
        }
    }
    
        4
  •  11
  •   Community T.Woody    7 年前

    这可以做到 更容易使用奇妙 XStream 图书馆。没有包装纸,没有注释。

    目标XML

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

    序列化

    ( String 使用小写可以避免别名 string 标签,但我用了操作码)

    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堵漏实例 here

        5
  •  10
  •   Jérôme Verstrynge Mark    12 年前

    来自个人博客 post ,不需要创建特定的 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>
    
        6
  •  8
  •   LiorH    15 年前

    我遇到过这种模式几次,我发现最简单的方法是用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),我将返回以包装器集为其实体的响应对象。

        7
  •  3
  •   petrsyn    9 年前

    最后我用 JacksonJaxbJsonProvider 你的春天不需要什么变化 context.xml 和Maven pom.xml

    在你的春天 上下文XML 添加 jacksonjaxbjson提供商 <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>
    
        8
  •  2
  •   piepera    14 年前

    用户1的例子对我很有用。但是,作为一个警告,除了简单的字符串/整数类型外,它不适用于其他任何类型,除非添加@xmlseealso注释:

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

    虽然这可以防止我在整个应用程序中使用一个通用列表类,但它仍然可以正常工作。它也可以解释为什么这个看似明显的类不存在于JAXB包中。

        9
  •  0
  •   Maggy    11 年前

    确保添加@xmlseealso标记,并在jaxblist中使用您的特定类。它非常重要,否则将引发httpmessagenotwrittableexception

        10
  •  0
  •   mstrthealias    9 年前

    如果我发现 RestEasy Jackson提供商 越快越好。

    只需添加 RestEasy Jackson提供商JAR . 没有实体包装。没有XML注释。没有自定义消息正文编写器。

        11
  •  0
  •   user7455210    8 年前

    如果在Jersey项目中使用Maven,请在pom.xml中添加以下内容并更新项目依赖项,以便JAXB能够检测模型类并将列表转换为媒体类型应用程序XML:

    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-core</artifactId>
        <version>2.2.11</version>
    </dependency>
    
        12
  •  0
  •   Gonen I    6 年前

    对于更一般的解决方案,对于任何顶层列表的JAXB-XML序列化(只需要编写一个新类),请查看此问题中给出的解决方案:

    Is it possible to programmatically configure 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();
    
    }