2011-08-08 1 views
1

Tomcat 7に展開するRestEasy 2.2.2を使用してJAX-RS Webサービスを開発しています。サービスはJAXBを使用してXMLを返します(返す必要があります)。返されたXMLは、それが次のコードで使用される方法に類似のConcurrentHashMapの表現が含まれている必要があります。RestEasyとTomcatを使用してHashMapで動作するMessageBodyWriterを取得するには?

@XmlRootElement(name="items") 
@XmlAccessorType(XmlAccessType.NONE) 
public class ItemCollection 
{ 
    @XmlElement(name="item") 
    private ConcurrentHashMap<String, Item> items; 

    public ItemCollection() 
    { 
     items = new ConcurrentHashMap<String, item>(); 
     // fill the map 
    } 
} 

Itemクラスはまた、XMLにシリアル化する必要がConcurrentHashMapが含まれています。

これは、リソースクラスである:

@Path("/items") 
public class ItemResource 
{ 
    @GET 
    @Produces(MediaType.APPLICATION_XML) 
    public ItemCollection getAllItems() 
    { 
     // get itemManager 
     return itemManager.getItems(); // ItemManager holds an instance of ItemCollection 
    } 
} 

このコードが実行されますが、コンテンツのないXMLを生成します。

<items> 
    <item/> 
</items> 

私は、出力のようなものであるとして取得しようとしています何:

<items> 
    <item id="..."> 
     <data>...</data> 
     <otheritems> 
      <otheritem id="..."> 
       <someotherdata>...</someotherdata> 
      </otheritem> 
     </otheritems> 
    </item> 
    <item id="..."> 
     <data>...</data> 
     <otheritems> 
      <otheritem id="..."> 
       <someotherdata>...</someotherdata> 
      </otheritem> 
      <otheritem id="..."> 
       <someotherdata>...</someotherdata> 
      </otheritem> 
      <otheritem id="..."> 
       <someotherdata>...</someotherdata> 
      </otheritem> 
     </otheritems> 
    </item> 
</items> 

MessageBodyWriter実装が必要であることがわかりました。組み込み機能i十分ではありません。 ConcurrentHashMapをマーシャリングするためにMessageBodyWriter実装を試しましたが、これまでのところ動作できませんでした(つまり、コードを呼び出すことはできますが、さまざまな例外を除いて停止します)。

MessageBodyWriter(およびMessageBodyReader)インターフェイスをどのように実装して使用する必要があるのか​​よく分かりません。ビル・バークの「RESTful Java with JAX-RS」という本があります。これは、JAX-RSサービスの設計を支援するという点で非常に便利ですが、関連するセクションのMessageBodyWriterの機能について十分な詳細を見つけることができませんでした。私のインターネット検索は正しい方向に私を導くことができるものをもたらしませんでした。

MessageBodyWriter(およびMessageBodyReader)のインターフェイスを正しく実装する方法を理解することができれば、嬉しいです。私は注釈が欠落しているか、誤っているのか、まったく新しいアプローチが必要かどうかは分かりません。

ご協力いただきありがとうございます。

EDIT:

次のコードを変更するには、途中でそこに私を取得します。

@XmlRootElement(name="items") 
@XmlAccessorType(XmlAccessType.NONE) 
public class ItemCollection 
{ 
    private ConcurrentHashMap<String, Item> items; 

    public ItemCollection() 
    { 
     items = new ConcurrentHashMap<String, item>(); 
     // fill the map 
    } 

    @XmlElement(name="item") 
    public Collection<Item> getItems() 
    { 
     return items.values(); 
    } 
} 

これは私が(サンプルは、上記の含まれている)必要があるXMLを生成します。しかし、このコードはアンマーシャリングに関しては機能しません。

java.lang.UnsupportedOperationException 
java.util.AbstractCollection.add(AbstractCollection.java:221) 
    com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Lister.java:290) 
com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Lister.java:254) 
com.sun.xml.internal.bind.v2.runtime.unmarshaller.Scope.add(Scope.java:106) 
com.sun.xml.internal.bind.v2.runtime.property.ArrayERProperty$ReceiverImpl.receive(ArrayERProperty.java:195) 
com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.endElement(UnmarshallingContext.java:507) 
com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(SAXConnector.java:145) 
com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:601) 
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1782) 
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2938) 
com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648) 
com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:140) 
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511) 
com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808) 
com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737) 
com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119) 
com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205) 
com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522) 
com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:200) 
com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:173) 
javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:137) 
javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:142) 
javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:151) 
javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:169) 
// ... the rest 

私は理由がアンマーシャラーがCollectionにアイテムを追加しようとしないように、「適切なセッター」の欠如であると仮定しますが、私はそのセッターは次のようになりますかわからない:私は次の例外を取得します。誰かがこれを行う方法を知っていれば、私は助けていただければ幸いです。

ありがとうございます。

EDIT 2:

ところで、私は彼が@XmlJavaTypeAdapterを使用することを提案しているクリスの回答を見てきました。私はこの提案を試してみて、必要なXMLに近いものを手に入れました。しかし、@ XmlJavaTypeAdapterを使用して取得したXMLには、余分なレベル(<items><item>の代わりに<class><items><item> ---例のように、クラスのメンバー変数としてConcurrentHashMapインスタンスがあります)があります。また、個々のマップアイテムの要素名を変更することもできません(これらは常に「アイテム」と呼ばれます)。

これは大きな問題ではありません。私は必要に応じて必要な変更を行い、それらと一緒に暮らすことができます。しかし、可能であれば、私は最初にそれらを持っていないようにしたいと思います。教育目的のために、EDIT 1のコードが非整列化(および可能であれば修正する方法)のために機能しない理由を理解したいと思います。

ご協力いただきありがとうございます。

答えて

1

あなたの問題は、JAXBとMessageBodyReader/MessageBodyWriterを混ぜようとしていることです。あなたはJAXBオブジェクトを持っているので、RestEasyがあなたのJAXBオブジェクトを考慮しないように、あなたのMessageBodyWriterを使ってすべてのシリアライゼーションを本当にしないようにしてください。このようにすることもできますが、オブジェクトモデルの残りの部分もシリアル化する必要があります。

MessageBodyReader/Writerは、おそらくそれがあまり意味がない理由であろうと、Streamsを扱うように調整されています。 XMLに進むという前提はありません。

おそらく、マップのJAXB XmlJavaTypeAdapterを作成し、JAXBにXML作成をさせたいと考えています。

http://download.oracle.com/javase/6/docs/api/javax/xml/bind/annotation/adapters/XmlJavaTypeAdapter.html

私はメトロメーリングリストhereに対するこの上の一つの良い記事を見つけた:あなたはJavaDocのページでより多くの情報を見つけることができます。このコードはあなたが望むものを与えるはずです。コンテキストはJAX-WSの周りにありましたが、カスタムバインディングを行うためのJAXBアノテーションを特に探しています。

@XmlRootElement 
public class Root 
{ 
    private String name; 
    private int junk; 

    @XmlJavaTypeAdapter(MapAdapter.class) 
    private Map<String, Boolean> lights; 

    public Root() 
    { 
    name = ""; junk = 0; lights = new HashMap<String, Boolean>(); 
    } 

    public void setName(String newName) { name = newName; } 
    public String getName()    { return name; } 

    public void setJunk(int newJunk) { junk = newJunk; } 
    public int getJunk()    { return junk; } 

    public void turnLightOn(String lightName) { lights.put(lightName, true); } 
    public void turnLightOff(String lightName) { lights.put(lightName, false); } 
} 

class MapAdapter extends XmlAdapter<MapElements[], Map<String, Boolean>> 
{ 
    public MapElements[] marshal(Map<String, Boolean> arg0) throws Exception 
    { 
    MapElements[] mapElements = new MapElements[arg0.size()]; 

    int i = 0; 
    for (Map.Entry<String, Boolean> entry : arg0.entrySet()) 
     mapElements[i++] = new MapElements(entry.getKey(), entry.getValue()); 

    return mapElements; 
    } 

    public Map<String, Boolean> unmarshal(MapElements[] arg0) throws Exception 
    { 
    Map<String,Boolean> r = new HashMap<String,Boolean>(); 
    for(MapElements mapelement : arg0) 
     r.put(mapelement.key, mapelement.value); 
    return r; 
    } 
} 

class MapElements 
{ 
    @XmlElement public String key; 
    @XmlElement public Boolean value; 

    private MapElements() {} //Required by JAXB 

    public MapElements(String key, Boolean value) 
    { 
    this.key = key; 
    this.value = value; 
    } 
} 
+0

この詳細な返信を書き留めていただきありがとうございます。私はそれを試して、それは私が必要なXMLの近くに私を取得します。私はアダプタを実装している間にカップルの問題にぶつかり、それらを含めるために私の投稿を編集しました。あなたがすばやく見て、何かを提案できるかどうかを知ることができたら、私は感謝します。 – alokoko

0

XmlJavaTypeAdapterメソッドは、JAXBでシリアライズとバックを行うことができます。しかし、それは私に必要なXMLを与えるものではありません。私はそれがあまり重要ではないと思ったが、結局のところ、その形でXMLを取得する必要があることが判明した。

@XmlJavaTypeAdapterを使用して取得するXMLには、余分なレベルがあります(<collection><item><item>の代わりに<collection><mapitem><item><mapitem><item>に似ています)。私はこの問題を説明するフォーラムに投稿を見つけました(このリンクを追加するとリンクが追加されます)。このタイプのXMLを取得するには、JAXBが地図ではなくコレクションを表示する必要があることを示します。

だから、それは他の人に有用であろうことを期待して、ここで私がやったことだ:

まず、私はこのインタフェースを定義した:

public interface MyCollectionInterface 
{ 
    public String getItemId(); 
} 

その後、私は中に置かれるべき項目を修正コレクションには、次のように:

public class CollectionItem implements MyCollectionInterface 
{ 
    @XmlAttribute 
    private String id; // This was already a class member 

    @Override 
    public String getItemId() 
    { 
     return id; 
    } 
} 

アイデアはHashMapで使用するキーを取得するための既知の方法を持っていることです。

@XmlRootElement(name="items") 
@XmlAccessorType(XmlAccessType.NONE) 
public class ItemCollection 
{ 
    @XmlElement(name="item") 
    private MyDictionary<CollectionItem> items; 

    public ItemCollection() 
    { 
     items = new MyDictionary<CollectionItem>(); 
    } 
} 

これは私が後だったXMLを生成します(私の元の記事を参照してください。

は、その後、次のコードをフォームに修正し、今

public class MyCollection<E extends MyCollectionInterface> extends AbstractSet<E> 
{ 
    private ConcurrentHashMap<String, E> items; 

    public MyCollection() 
    { 
     items = new ConcurrentHashMap<String, E>(); 
    } 

    @Override 
    public boolean add(E e) 
    { 
     return items.putIfAbsent(e.getItemId(), e) != null; 
    } 

    @Override 
    public Iterator<E> iterator() 
    { 
     return items.values().iterator(); 
    } 

    @Override 
    public int size() 
    { 
     return items.size(); 
    } 

    // other functionality as needed 
} 

MyCollectionの宣言が来た場所にすべてを置きます例として)。

関連する問題