JAXBを使用して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();
}
複数のクラスの要素を含むリストのリストラッパーを作成し、Xラッパークラスを作成せずにクラスタイプに応じて個々のXmlElement名を指定する場合は、 @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&lt;を作成する必要はありません。 T&gt;
オブジェクト。
文字列のリストを持つオブジェクトを想定:
@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 ライブラリを使用して、非常に簡単に実行できます。ラッパーもアノテーションもありません。
ターゲットXML
<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インスタンスはスレッドセーフであり、事前に構成できるため、コード量が1ライナーに縮小されることに注意してください。
XStreamは、JAX-RSサービスのデフォルトのシリアル化メカニズムとしても使用できます。 Jerseyで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
で、&lt; jaxrs:server&gt;
に JacksonJaxbJsonProvider
を追加します:
<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の例は私にとってはうまくいきました。ただし、警告として、@ 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();
}