2011-08-25 30 views
6

我是一個新手,他有一個比較好的開始,直到我決定玩一些複雜的對象。我碰到的問題是關於解組到服務器的對象(在服務器端從XML創建對象)。CXF Restful服務複合對象解組不起作用

以下是我的示例(代表性)服務實現。

這是我的「複雜對象」數據類型。

package data; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
public class ComplexType { 
    private long id; 
    private String name; 
    private Boolean isRegistered; 


    public ComplexType() { 
     super(); 
    } 
    public ComplexType(long id, String name, Boolean isRegistered) { 
     super(); 
     this.id = id; 
     this.name = name; 
     this.isRegistered = isRegistered; 
    } 
    public long getId() { 
     return id; 
    } 
    public void setId(long id) { 
     this.id = id; 
    } 
    public String getName() { 
     return name; 
    } 
    public void setName(String name) { 
     this.name = name; 
    } 
    public Boolean getIsRegistered() { 
     return isRegistered; 
    } 
    public void setIsRegistered(Boolean isRegistered) { 
     this.isRegistered = isRegistered; 
    } 
} 

這是我的服務API

package api; 

import javax.ws.rs.FormParam; 
import javax.ws.rs.GET; 
import javax.ws.rs.POST; 
import javax.ws.rs.Path; 
import javax.ws.rs.PathParam; 

import data.ComplexType; 


public interface Service { 
    @GET 
    @Path("/nummembers") 
    int getNumElements(); 

    @GET 
    @Path("/member/{id}") 
    ComplexType getMember(@PathParam("id") long id); 

    @POST 
    @Path("/member") 
    boolean addMember(@FormParam("member") ComplexType member); 
} 

這是服務的實現:

package impl; 

import java.util.HashMap; 
import java.util.Map; 

import data.ComplexType; 
import api.Service; 

public class ServiceImpl implements Service { 
    Map<Long, ComplexType> data; 

    public ServiceImpl() { 
     System.out.println("TestApp Starting"); 
     data = new HashMap<Long, ComplexType>(); 
    } 

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

    @Override 
    public ComplexType getMember(long id) { 
     if (data.containsKey(id)) { 
      return data.get(id); 
     } 
     ComplexType ct = 
      new ComplexType(id, "NAME" + new Long(id).toString(), (id % 2 == 1)); 
     data.put(id, ct); 
     return ct; 
    } 

    @Override 
    public boolean addMember(ComplexType member) { 
     int preSize = data.size(); 
     data.put(member.getId(), member); 
     return preSize < data.size(); // True if added 
    } 
} 

所以,當我打電話getNumElements()是沒有問題的。當我打電話給getMember(long id)時,我得到一個序列化的「ComplexType」就好了。當我序列化複雜類型,並把它作爲FormParam到addMember(ComplexType member),我總是Parameter Class data.ComplexType has no constructor with single String parameter, static valueOf(String) or fromString(String) methods

我試圖爲我定製的供應商,通過編寫以下類:

package impl; 

import javax.ws.rs.ext.ContextResolver; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.JAXBException; 

import data.ComplexType; 

public class JaxbXmlContextResolver implements ContextResolver<Object> { 
    private static final Class<?>[] classes = new Class[] {ComplexType.class}; 
    private static final JAXBContext context = initContext(); 

    public static JAXBContext initContext() { 
     JAXBContext context = null; 
     try { 
      context = JAXBContext.newInstance(classes); 
     } catch (JAXBException e) { 
      throw new RuntimeException(e); 
     } 
     return context; 
    } 

    @Override 
    public Object getContext(Class<?> arg0) { 
     return context; 
    } 
} 

而對於配置的其餘部分,這裏是我的web.xml:

<?xml version="1.0"?> 
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
"http://java.sun.com/dtd/web-app_2_3.dtd"> 

<web-app> 
    <display-name>TestApp</display-name> 

    <context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>classpath:cxf.xml</param-value> 
    </context-param> 

    <context-param> 
    <param-name>log4jConfigLocation</param-name> 
    <param-value>classpath:log4j.properties</param-value> 
    </context-param> 

    <listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
    </listener> 

    <servlet> 
    <servlet-name>CXFServlet</servlet-name> 
    <display-name>CXF Servlet</display-name> 
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> 
    <load-on-startup>1</load-on-startup> 
    </servlet> 

    <servlet-mapping> 
    <servlet-name>CXFServlet</servlet-name> 
    <url-pattern>/services/*</url-pattern> 
    </servlet-mapping> 
</web-app> 

而且cxf.xml它指的是:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:util="http://www.springframework.org/schema/util" 
     xmlns:jaxrs="http://cxf.apache.org/jaxrs" 
     xmlns:jaxws="http://cxf.apache.org/jaxws" 
     xmlns:cxf="http://cxf.apache.org/core" 
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans.xsd 
          http://www.springframework.org/schema/util 
          http://www.springframework.org/schema/util/spring-util-2.0.xsd 
          http://cxf.apache.org/jaxrs 
          http://cxf.apache.org/schemas/jaxrs.xsd"> 

    <import resource="classpath:META-INF/cxf/cxf.xml" /> 
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> 

    <bean id="myservice" class="impl.ServiceImpl" /> 
    <bean id="jaxbXmlProvider" class="impl.JaxbXmlContextResolver" /> 

    <jaxrs:server id="connectionService" address="/" > 
    <jaxrs:serviceBeans> 
     <ref bean="myservice" /> 
    </jaxrs:serviceBeans> 
    <jaxrs:extensionMappings> 
     <entry key="xml" value="application/xml" /> 
    </jaxrs:extensionMappings> 
    <jaxrs:providers> 
     <ref bean="jaxbXmlProvider" /> 
    </jaxrs:providers> 
    </jaxrs:server> 
</beans> 

爲了完整起見,這裏是我用來構建應用程序的pom.xml。

<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 

    <properties> 
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    </properties> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>TESTAPP</groupId> 
    <artifactId>testApp</artifactId> 
    <packaging>war</packaging> 
    <version>1.0</version> 
    <name>Test Application</name> 
    <url>http://www.mycompany.com</url> 

    <dependencies> 
    <dependency> 
     <groupId>log4j</groupId> 
     <artifactId>log4j</artifactId> 
     <version>1.2.16</version> 
    </dependency> 

    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-orm</artifactId> 
     <version>3.0.5.RELEASE</version> 
    </dependency> 

    <dependency> 
     <groupId>org.apache.cxf</groupId> 
     <artifactId>cxf-rt-frontend-jaxrs</artifactId> 
     <version>2.4.1</version> 
     <exclusions> 
     <exclusion> 
      <groupId>wsdl4j</groupId> 
      <artifactId>wsdl4j</artifactId> 
     </exclusion> 
     </exclusions> 
    </dependency> 
    <dependency> 
     <groupId>com.thoughtworks.xstream</groupId> 
     <artifactId>xstream</artifactId> 
     <version>1.3.1</version> 
    </dependency> 
    <dependency> 
     <groupId>net.sf.kxml</groupId> 
     <artifactId>kxml2</artifactId> 
     <version>2.2.2</version> 
    </dependency> 
    <dependency> 
     <groupId>javax.xml.bind</groupId> 
     <artifactId>jaxb-api</artifactId> 
     <version>2.2.1</version> 
    </dependency> 

    </dependencies> 

    <build> 
    <plugins> 
     <plugin> 
     <artifactId>maven-compiler-plugin</artifactId> 
     <version>2.3.2</version> 
     <configuration> 
      <source>1.6</source> 
      <target>1.6</target> 
     </configuration> 
     </plugin> 
    </plugins> 
    </build> 
</project> 

任何幫助(溶液,指針,或任何種類的方向),將不勝感激。

哦,我正在使用cxf 2.4.1和Spring 3.0.5.RELEASE。這是我部署的應用程序的精確副本。

謝謝。

+0

據:http://cxf.547215.n5.nabble.com/MessageBodyReader-not-picked-up-td564496.html標記爲@FormParam的參數不由消息閱讀器處理,並且需要特定的ParameterHandler。所以,我寫了一個自定義處理程序,它運行良好。令我困擾的是,我必須爲每個obj編寫一個處理程序,我希望能夠通過一種形式接收它。如果有解決方案,我將不勝感激。另外,我的擔心之一是,我想在我的服務中支持xml和json。我不知道如何爲相同的obj編寫兩個處理程序,或者是否應該編寫處理程序來處理這兩種格式。 –

回答

0

爲三個屬性中的每一個實現一個沒有參數的默認構造函數和一個帶有參數的默認構造函數。這應該會讓JAXB開心。

+0

謝謝。但那並不奏效。我實際上有兩個構造函數,但忘記將它們添加到我的簡化示例中。 –

1

正如http://cxf.547215.n5.nabble.com/MessageBodyReader-not-picked-up-td564496.html所述,事實證明我需要一個ParameterHandler。由於我在應用程序中有一大組對象,因此我不想爲每個對象創建單獨的ParameterHandler,因此我做了一個小改動:

使用JAXB inheritance, unmarshal to subclass of marshaled class中描述的技術,我創建了一個基類型「BaseType 「所有的API數據對象(TypeA,TypeB,...)都被繼承了。

public class XmlParamHandler implements ParameterHandler<BaseType> { 
    private static final Logger log = LoggerFactory.getLogger(XmlParamHandler.class); 
    private static final JAXBContext jaxbContext = initContext(); 


    private static JAXBContext initContext() throws JAXBException { 
     Class<?>[] classes = new Class[] { 
      com.mycompany.BaseType.class, 
      com.mycompany.TypeA.class, 
      com.mycompany.TypeB.class, 
      com.mycompany.TypeC.class, 
      com.mycompany.TypeD.class, 
      com.mycompany.TypeE.class, 
      com.mycompany.TypeF.class, 
     }; 
     JAXBContext context = JAXBContext.newInstance(classes); 
     return context; 
    } 

    public static <T> T valueOf(String str) throws JAXBException { 
     if (str == null) { 
      return null; 
     } 
     Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 
     StringReader sr = new StringReader(str); 
     @SuppressWarnings("unchecked") 
     T request = (T) unmarshaller.unmarshal(sr); 
     return request; 
    } 

    @Override 
    public BaseType fromString(String s) { 
     BaseType ct = null; 
     try { 
      return valueOf(s); 
     } catch (JAXBException e) { 
      return null; 
     } 
    } 
} 
0

將一個複雜的「XML-marshallable」對象作爲@FormParam傳遞是有點奇怪的。 @FormParam主要用於簡單或原始類型,如字符串,整數等。

JAX-RS的好處之一是它會自動爲你準備/取消整理對象是你告訴它要使用哪種媒體類型。所以,你可能要定義您的端點爲(加@Consumes,在成員沒有註釋):

@POST 
@Path("/member") 
@Consumes({ MediaType.APPLICATION_XML }) 
boolean addMember(ComplexType member); 

和POST成員的XML表示作爲請求主體而非形式參數和集請求的Content-Type標題爲application/xml。然後CXF將自動讀取請求主體並將其解析爲ComplexType的一個實例並將其傳遞給您的方法。

您可以使用捲曲。創建一個序列化對象(member.xml)的XML文件並運行:

curl -k --request POST --header "Content-Type: application/xml" --data @member.xml https://localhost:8080/path/to/service