CXF Restful服务复杂对象解组不起作用

时间:2011-08-25 19:42:24

标签: java jaxb cxf

我是宁静的服务新手,并且有一个相对良好的开端,直到我决定玩一些复杂的对象。我遇到的问题是解组一个到服务器的对象(在服务器端从XML创建对象)。

以下是我的服务示例(代表性)实现。

这是我的“复杂对象”数据类型。

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。这是我部署的应用程序的精确副本。

感谢。

3 个答案:

答案 0 :(得分:1)

正如http://cxf.547215.n5.nabble.com/MessageBodyReader-not-picked-up-td564496.html所述,事实证明我需要一个ParameterHandler。由于我的应用程序中有大量对象,因此我不想创建单独的ParameterHandler&lt;&gt;对于每一个,所以我做了一个小改动:

使用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;
        }
    }
}

答案 1 :(得分:0)

实现一个没有参数的默认构造函数和一个带有三个属性中每个属性的参数的构造函数。这应该让JAXB满意。

答案 2 :(得分:0)

将一个复杂的“XML-marshallable”对象作为@FormParam传递出来有点奇怪。 @FormParam主要用于简单或原始类型,如字符串,整数等。

JAX-RS的一个好处是,它会自动为你配置/解组对象你告诉它要使用哪种媒体类型。因此,您可能希望将端点定义为(在成员上添加@Consumes而不添加注释):

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

将成员的XML表示形式发布为请求正文而不是表单参数,并将请求的Content-Type标头设置为{{1 }}。然后,CXF将自动读取请求体并将其解析为ComplexType实例,并将其传递给您的方法。

你可以使用curl。使用序列化对象(member.xml)创建XML文件并运行:

application/xml