web-dev-qa-db-ja.com

JAXBを使用してList <String>を非整列化/整列化する

私は非常に単純なRESTサーバーを作成しようとしています。文字列のリストを返すテストメソッドがあります。次のコードです。


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

次のエラーが発生します。

 SEVERE:Java type、
 class Java.util.Vector、およびMIMEメディアタイプ、
 application/octet-のメッセージ本文ライターストリーム、見つかりませんでした

私は、JAXBにString、Integerなどの単純な型のデフォルト設定があることを望んでいました。私が想像したものは次のとおりです。


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

この方法を機能させる最も簡単な方法は何ですか?

45
User1

@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パッケージにこの単純なクラスが存在しないのはなぜですか?誰か他の場所でそのようなものを見ていますか?

46
User1
@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();
}
32
Sample Code

複数のクラスの要素を含むリストのリストラッパーを作成し、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;
    }
}
12
Zounadire

個人のブログ 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>
11

これは、素晴らしい XStream ライブラリを使用して簡単に行うことができます[〜#〜] much [〜#〜]。ラッパーもアノテーションもありません。

ターゲット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をプラグインする例があります here

10

私はこのパターンに何度か遭遇しましたが、最も簡単な方法は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オブジェクトを返します

8
LiorH

最後に、JacksonJaxbJsonProviderを使用して解決しました。Springcontext.xmlおよびMaven pom.xmlの変更はほとんど必要ありません。

Spring context.xml<jaxrs:server>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>
3
petrsyn

User1の例は私にとってはうまくいきました。ただし、警告として、@ XmlSeeAlso注釈を追加しない限り、単純な文字列/整数型以外では機能しません。

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

これは正常に機能しますが、アプリケーション全体で単一の汎用リストクラスを使用することはできません。また、この一見明らかなクラスがJAXBパッケージに存在しない理由も説明できます。

2
piepera

Resteasy Jackson Provider早く見つけたら時間を節約できたでしょう。

Resteasy Jackson Provider JARを追加するだけです。エンティティラッパーはありません。 XMLアノテーションはありません。カスタムメッセージ本文ライターはありません。

0
mstrthealias

Jerseyプロジェクトでmavenを使用している場合、pom.xmlに以下を追加し、プロジェクトの依存関係を更新して、Jaxbがモデルクラスを検出し、リストをメディアタイプアプリケーションXMLに変換できるようにします。

<dependency>
    <groupId>com.Sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.2.11</version>
</dependency>
0
user7455210

JaxbList内で使用する特定のクラスに@XmlSeeAlsoタグを追加してください。 HttpMessageNotWritableExceptionをスローすることは非常に重要です

0
Maggy

より一般的なソリューションについては、トップクラスのJAXB-XMLシリアル化で、新しいクラスを1つ記述するだけでよいため、この質問にあるソリューションをチェックしてください。

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

}
0
Gonen I