簡體   English   中英

如何使用RestEasy和Tomcat使MessageBodyWriter與HashMap一起使用?

[英]How to get MessageBodyWriter to work with a HashMap using RestEasy and Tomcat?

我正在使用RestEasy 2.2.2開發一個JAX-RS Web服務,以將其部署到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類還包含一個ConcurrentHashMap ,需要將其序列化為XML。

這是資源類:

@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 (和MessageBodyReader )接口。 我有Bill Burke的“ RESTful Java with JAX-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實例作為成員一類的變量)。 我似乎也無法更改各個地圖項的元素名稱(它們始終稱為“ item”)。

這些不是大問題,我可以進行必要的更改,並在必要時與他們一起生活。 但是,如果可能的話,我不想首先使用它們。 出於教育目的,我還想了解為什么EDIT 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

我發現在這一個很好的職位在地鐵郵件列表在這里 此代碼應為您提供所需的內容。 上下文是關於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; 
  } 
} 

XmlJavaTypeAdapter方法可以正常工作,因為它允許JAXB進行序列化並返回。 但是,它沒有給我所需的XML。 我以為那沒什么大不了的,但是最后我發現我需要以這種形式獲取XML。

我使用@XmlJavaTypeAdapter獲得的XML有一個附加級別(類似於<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(請參閱我的原始帖子以獲取示例)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM