[英]Using JAXB to unmarshal/marshal a List<String>
我正在嘗試創建一個非常簡單的 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>
使這種方法起作用的最簡單方法是什么?
我使用了@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 包中不存在這個簡單的類? 有人在其他地方看到類似的東西嗎?
@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();
}
如果你們中的任何人想要為包含多個類的元素的列表編寫一個列表包裝器,並且想要根據 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>
或者,您可以使用@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;
}
}
從個人博客文章來看,沒有必要創建特定的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>
使用出色的XStream庫可以更輕松地完成此操作。 沒有包裝,沒有注釋。
<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 服務的默認序列化機制。 在澤西島插入 XStream 的示例可以在這里找到
我遇到過幾次這種模式,我發現最簡單的方法是使用 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 對象,並將包裝器設置為其實體
最后,我使用JacksonJaxbJsonProvider
解決了它需要在 Spring context.xml
和 Maven pom.xml
中進行一些更改
在您的 Spring 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>
User1 的示例對我來說效果很好。 但是,作為警告,除了簡單的 String/Integer 類型之外,它不能用於任何其他類型,除非您添加 @XmlSeeAlso 注釋:
@XmlRootElement(name = "List")
@XmlSeeAlso(MovieTicket.class)
public class MovieTicketList {
protected List<MovieTicket> list;
這可以正常工作,盡管它阻止我在整個應用程序中使用單個通用列表類。 這也可以解釋為什么 JAXB 包中不存在這個看似顯而易見的類。
確保使用 JaxbList 中使用的特定類添加 @XmlSeeAlso 標記。 這很重要,否則它會拋出 HttpMessageNotWritableException
如果我早點找到Resteasy Jackson Provider ,我會節省時間。
只需添加Resteasy Jackson Provider JAR 。 沒有實體包裝器。 沒有 XML 注釋。 沒有自定義消息正文作者。
如果您在 jersey 項目中使用 maven,請在 pom.xml 中添加以下內容並更新項目依賴項,以便 Jaxb 能夠檢測模型類並將列表轉換為媒體類型應用程序 XML:
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
對於更通用的解決方案,對於任何頂級列表的 JAXB-XML 序列化,只需要編寫 1 個新類,請查看此問題中給出的解決方案:
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();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.