2

私はRestEasy2.2.2を使用してJAX-RSWebサービスを開発してTomcat7にデプロイしています。サービスはJAXBを使用してXMLを返します(返す必要があります)。返されるXMLには、次のコードで使用されているのと同様のConcurrentHashMapの表現が含まれている必要があります。

@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クラスには、ConcurrentHashMapXMLにシリアル化する必要のあるも含まれています。

これはリソースクラスです:

@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組み込みの機能が不十分な場合は、実装が必要であることがわかりました。MessageBodyWriterをマーシャリングするための実装を考え出そうとしましたがConcurrentHashMap、これまでは機能させることができませんでした(つまり、呼び出されるコードを取得できますが、さまざまな例外を除いて停止します)。

インターフェースをどのようにMessageBodyWriter(そして)どのように実装して使用すべきかを私は完全には理解していないようです。MessageBodyReaderBillBurkeの「RESTfulJavawithJAX-RS」の本を持っています。JAX-RSサービスの設計に役立つという点では非常に便利ですがMessageBodyWriter、関連するセクションで機能に関する十分な詳細を見つけることができませんでした。私のインターネット検索でも、私を正しい方向に導くことができるものは何も得られませんでした。

MessageBodyWriter(およびMessageBodyReader)インターフェースを適切に実装する方法を誰かが理解するのを手伝ってくれれば幸いです。注釈が欠落しているのか、置き忘れているのか、まったく新しいアプローチが必要なのかはわかりません。

助けてくれてありがとう。

編集:

コードを次のように変更すると、途中で次のようになります。

@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だと思いますが、そのセッターがどのように見えるかはわかりません。誰かがこれを行う方法を知っているなら、私は助けていただければ幸いです。

前もって感謝します。

編集2:

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

これらは大きな問題ではなく、必要な変更を加えて、必要に応じて一緒に暮らすことができます。でも、できればそもそも持っていたくないです。教育目的で、EDIT 1のコードがアンマーシャリングで機能しない理由(および可能であれば修正方法)も理解したいと思います。

すべての助けを事前に感謝します。

4

2 に答える 2

1

あなたの問題は、JAXB と MessageBodyReader/MessageBodyWriter を混在させようとしていることだと思います。JAXB オブジェクトがあるため、RestEasy が MessageBodyWriter を使用してすべてのシリアライゼーションを実行することは望ましくありません。JAXB オブジェクトが考慮されないからです。このようにすることもできますが、オブジェクト モデルの残りの部分もシリアル化する必要があります。

MessageBodyReader/Writer は Streams での作業を対象としているため、あまり意味がありませんでした。XML を使用することを想定していません。

おそらくやりたいことは、マップ用の JAXB XmlJavaTypeAdapter を作成し、JAXB に XML の作成を任せることです。詳細については、次の JavaDoc ページを参照してください。

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

これについては、Metro メーリング リストhere で1 つの良い投稿を見つけました。このコードは、あなたが望むものを提供するはずです。コンテキストは 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; 
  } 
} 
于 2011-08-08T13:16:58.903 に答える
0

このXmlJavaTypeAdapterメソッドは、JAXB がシリアライゼーションとその逆を行うことができるという点で問題なく機能します。しかし、必要な XML が得られません。それはあまり問題にならないだろうと思っていましたが、最終的にはその形式で XML を取得する必要があることがわかりました。

私が使用する XML に@XmlJavaTypeAdapterは、余分なレベルがあります (<collection><mapitem><item><mapitem><item>の代わりに似ています<collection><item><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です。

その後、宣言が出されましたMyCollection

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
}

ここで、コードを次の形式に変更すると、すべてが適切に配置されます。

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

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

これにより、私が求めていた XML が生成されます (例については、元の投稿を参照してください)。

于 2011-08-27T09:45:07.970 に答える