我正在尝试创建一个非常简单的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>
使这种方法有效的最简单方法是什么?
答案 0 :(得分:46)
我使用@ 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包中不存在这个简单的类?有人在别处看到类似的东西吗?
答案 1 :(得分:32)
答案 2 :(得分:12)
如果您想要为包含多个类的元素的列表编写列表包装器,并且想要根据Class类型给出单独的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>
Alternatevily你可以使用@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;
}
}
答案 3 :(得分:11)
使用精彩的XStream库可以轻松完成 MUCH 。没有包装,没有注释。
<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);
反序列化为String []
XStream xStream = new XStream();
xStream.alias("Strings", String[].class);
xStream.alias("String", String.class);
String[] result = (String[])xStream.fromXML(file);
请注意,XStream实例是线程安全的,可以预先配置,将代码量缩减为单行。
XStream还可以用作JAX-RS服务的默认序列化机制。可以在here
中找到在Jersey中插入XStream的示例答案 4 :(得分:11)
在个人博客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>
答案 5 :(得分:8)
我遇到过这种模式几次,我发现最简单的方法是用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对象,并将包装器设置为其实体
答案 6 :(得分:3)
最后我使用JacksonJaxbJsonProvider
解决了这个问题。你的Spring [{1}}和Maven context.xml
需要进行一些更改
在你的春天pom.xml
将context.xml
添加到JacksonJaxbJsonProvider
:
<jaxrs:server>
在Maven pom.xml中添加:
<jaxrs:server id="restService" address="/resource">
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
</jaxrs:providers>
</jaxrs:server>
答案 7 :(得分:2)
User1的例子对我很有用。但是,作为警告,除了添加@XmlSeeAlso注释之外,它不会用于除简单字符串/整数类型之外的任何其他内容:
@XmlRootElement(name = "List")
@XmlSeeAlso(MovieTicket.class)
public class MovieTicketList {
protected List<MovieTicket> list;
这可行,但它阻止我在整个应用程序中使用单个通用列表类。它也可以解释为什么在JAXB包中不存在这个看似明显的类。
答案 8 :(得分:0)
确保添加@XmlSeeAlso标记以及JaxbList中使用的特定类。它抛出HttpMessageNotWritableException
是非常重要的答案 9 :(得分:0)
如果我早点找到 Resteasy Jackson提供商,我会节省时间。
只需添加 Resteasy Jackson Provider JAR 即可。没有实体包装器。没有XML注释。没有自定义邮件正文编写者。
答案 10 :(得分:0)
如果你在jersey项目中使用maven,请在pom.xml中添加以下内容并更新项目依赖项,以便Jaxb能够检测模型类并将列表转换为媒体类型应用程序XML:
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
答案 11 :(得分:0)
对于更通用的解决方案,对于任何顶级列表的JAXB-XML序列化,只需要编写1个新类,请查看此问题中给出的解决方案:
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();
}