2011-08-08 5 views
1

Tomcat 7에 배포 할 RestEasy 2.2.2를 사용하여 JAX-RS 웹 서비스를 개발 중입니다. 서비스는 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) 인터페이스를 구현하고 사용해야하는 방법을 잘 모르겠습니다. 빌 버크 (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 

나는 이유는 Unmarshaller에이 Collection에 항목을 추가하려고하지 않습니다 있도록 "적절한 세터"의 부족이다 가정하지만 그 세터의 모습 방법을 모른다 : 나는 다음과 같은 예외가 . 누구든지이 작업을 수행하는 방법을 알고 있다면 도움을 주시면 감사하겠습니다.

미리 감사드립니다.

편집 2 : 그가 @XmlJavaTypeAdapter를 사용하여 제안 곳 BTW

, 나는 크리스 '답장을 보았다. 제안을 시도한 결과 필요한 XML에 가깝게 다가 왔습니다.그러나 @XmlJavaTypeAdapter를 사용하여 얻은 XML에는 대신 <class><items><item>이라는 추가 레벨이 있습니다. 예제에서 볼 수 있듯이 ConcurrentHashMap 인스턴스가 클래스의 멤버 변수로 사용됩니다. 또한 개별지도 항목의 요소 이름을 변경할 수없는 것처럼 보입니다 (항상 "항목"이라고 함).

위의 내용은 큰 문제가 아니며 필자는 필요한 경우 변경하고 필요에 따라 함께 살 수 있습니다. 그러나 가능하다면, 나는 처음부터 그것들을 갖고 싶지 않습니다. 교육적 목적을 위해서, 나는 EDIT 1의 코드가 언 마샬링 (unmarshalling)을 위해 작동하지 않는다는 것과 왜 가능하면 그것을 고치는 지 이해하고 싶다.

미리 도움을 청하십시오.

답변

1

여러분의 문제는 여러분이 MessageBodyReader/MessageBodyWriter와 JAXB를 섞으려고한다는 것입니다. 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 주석을 구체적으로 찾고 있습니다. 이 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 방법은 확인을 작동합니다. 그러나 필자가 필요로하는 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에 사용되는 키를 얻을 수있는 알려진 방법이하는 것입니다.

@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에 대한 선언을했다가 제자리에 모든 것을두고 예를 들어).

관련 문제