解决:Apache CXF无法自动转换JSON数据

时间:2022-10-29 19:37:34

问题描述

  Apache CXF是一个比较成熟的Java版Web Service框架,我们当前的项目中正在使用。它当然支持RESTful风格的Web Service,也支持JSON的数据格式。但是,在最新的3.0.4版本中,却无法自动将数据转换为JSON格式。
  像下面这样发布一个HelloWorld服务的话,是可以成功的。但是对于需要转换JSON数据的场景(通过JAXB方式或其他),它会报出错误。

package com.tech4j.demo;

import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;

import com.tech4j.demo.webservice.HelloWorld;
import com.tech4j.demo.webservice.HelloWorldImpl;

public class CxfJsonProviderDemo {

public static void main(String[] args) {
HelloWorld hello = new HelloWorldImpl();
JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
factory.setResourceClasses(HelloWorld.class);
factory.setResourceProvider(HelloWorld.class,
new SingletonResourceProvider(hello));
String address = "http://localhost:9000/MyService";
factory.setAddress(address);
factory.create();
}

}

  在这段代码中,HelloWorld接口定义了一个getPerson()方法,它返回一个Person类型的对象,并且使用@Produces注解声明其为JSON格式的数据。具体接口和实现类的代码略。
  如果通过浏览器访问地址http://localhost:9000/MyService/HelloWorld/getPerson,页面会提示说“No message body writer has been found for class com.tech4j.demo.webservice.Person, ContentType: application/json”。虽然服务发布成功,但是却无法将Person类型的对象转换为JSON数据。

解决方案

  这是由于新版本的CXF中已经不再提供默认的JSON Provider了,我们需要自己找一个第三方的Provider,设置给CXF框架,它才能够自动调用该Provider完成转换操作。
  我们需要在发布服务的时候,为CXF的服务factory设置这个第三方的Provider,例如Jackson。代码如下:

JacksonJaxbJsonProvider jaxbProvider = new JacksonJaxbJsonProvider();
factory.setProvider(jaxbProvider);

  这样一来,CXF就会自动调用这个Provider来完成符合JAXB标准的对象与JSON格式的转换工作。

Maven信息

Apache CXF

  CXF框架当前最新的Maven构建信息如下(可根据实际需要扩充或修改):

<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.0.4</version>
</dependency>

SLF4J-Log4j

  CXF默认不含有日志记录的组件,需要手动再引入SLF4J和/或Log4j的Maven依赖。如下所示:

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.10</version>
</dependency>

JacksonJsonProvider

  Jackson专门针对JAX-RS标准提供了JSON Provider,其Maven信息如下:

<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.5.2</version>
</dependency>

参考代码

HelloWorld.java

package com.tech4j.demo.webservice;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/HelloWorld")
public interface HelloWorld {

@GET
@Path("/getPerson")
@Produces(MediaType.APPLICATION_JSON)
public Person getPerson();

}

HelloWorldImpl.java

package com.tech4j.demo.webservice;

public class HelloWorldImpl implements HelloWorld {

@Override
public Person getPerson() {
return new Person("JavaHuan", 24);
}

}

Person.java

package com.tech4j.demo.webservice;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Person {

private String name;
private Integer age;

public Person() {
}

public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

}

  Person类中含有JAXB的注解配置,如@XmlRootElement。还可以添加其他的JAXB注解。