First time implementing JAX-RS Client API
in an application and I am having some small problems when it comes to storing the response data, which is returned as JSON
as a Java BEAN. Refer to the following code snippets below which demonstrate how I have implemented it thus far.
object = client.target(uri).request().post(Entity.entity(requestObject, APPLICATION_JSON), Object.class);
Essentially, I would like to store the returned JSON
response from the web service into my Java BEAN, which in this scenario is named object
. requestObject
is obviously the data which I am sending through to the web service, and I can confirm that POST does perform the operation successfully.
After the line of code from the above example I have a simple: object.toString();
call just to see the values currently stored within this object
. However, when this executes and printed out to the console, all the object
fields are printed out as null
, which I do not understand why. I have annotated my Java BEAN class with the following @XmlRootElement
above the class, although it still does not work. I do have another object nested as a variable in this Class, would that potentially be the reason why it does not correctly come through?
As an example, this is what my JSON returned object looks like when I invoke the web service through CLI curl
:
"response": {
"description": "test charge",
"email": "[email protected]",
"ip_address": "192.123.234.546",
"person": {
"name": "Matthew",
"address_line1": "42 Test St",
"address_line2": "",
"address_city": "Sydney",
"address_postcode": "2000",
"address_state": "WA",
"address_country": "Australia",
"primary": null
}
}
Any reasons why this could be happening?
UPDATE [Response Bean Class Below]
@XmlRootElement(name = "response")
public class ResponseObject {
// Instance Variables
private String description;
private String email;
private String ip_address;
private Person person;
// Standard Getter and Setter Methods below
Person Object which belongs to the ResponseObject Class
@XmlRootElement
public class Card {
// Instance Variables
private String name;
private String address_line1;
private String address_line2;
private String address_city;
private int address_postcode;
private States address_state;
private String address_country;
private String primary;
// Standard Getter and Setter Methods below
So I was able to reproduce the problem, and after a bit of testing, I realized that the problem was pretty obvious, if I had looked at it from the right perspective. I was originally looking at it from the view that maybe something was wrong with the provider configuration. But after a simple "Jackson only" test, just using the ObjectMapper
and trying the read the value, it became clear. The problem is with the json format and the structure of the classes.
Here is the structure
{
"response": {
"description": "test charge",
..
"person": {
"name": "Matthew",
..
}
}
}
And here are your classes
public class ResponseObject {
private String description;
private Person person;
...
}
public class Person {
private String name;
}
The problem with this is that the top level object is expecting just a single attribute response
. But our top level object is ResponseObject
, which has no attribute response
. With the ignore unknown properties on, the unmarhalling succeeds, because the only attribute is response
, for which no attribute exists, so nothing gets populated.
A simple (Json/JAXB friendly) fix would be to create a wrapper class, with a response
attribute of type ResponseObject
public class ResponseWrapper {
private ResponseObject response;
}
This will allow the unmarshalling to succeed
final ResponseWrapper ro = target.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(new ResponseWrapper()
, MediaType.APPLICATION_JSON_TYPE), ResponseWrapper.class);
ResponseObject
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "response")
public class ResponseObject {
private String description;
private String email;
private String ip_address;
private Person person;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getIp_address() {
return ip_address;
}
public void setIp_address(String ip_address) {
this.ip_address = ip_address;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
public String toString() {
return "ResponseObject{"
+ "\n description=" + description
+ "\n email=" + email
+ "\n ip_address=" + ip_address
+ "\n person=" + person
+ "\n }";
}
}
Person
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Person {
// Instance Variables
private String name;
private String address_line1;
private String address_line2;
private String address_city;
private int address_postcode;
private String address_state;
private String address_country;
private String primary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress_line1() {
return address_line1;
}
public void setAddress_line1(String address_line1) {
this.address_line1 = address_line1;
}
public String getAddress_line2() {
return address_line2;
}
public void setAddress_line2(String address_line2) {
this.address_line2 = address_line2;
}
public String getAddress_city() {
return address_city;
}
public void setAddress_city(String address_city) {
this.address_city = address_city;
}
public int getAddress_postcode() {
return address_postcode;
}
public void setAddress_postcode(int address_postcode) {
this.address_postcode = address_postcode;
}
public String getAddress_state() {
return address_state;
}
public void setAddress_state(String address_state) {
this.address_state = address_state;
}
public String getAddress_country() {
return address_country;
}
public void setAddress_country(String address_country) {
this.address_country = address_country;
}
public String getPrimary() {
return primary;
}
public void setPrimary(String primary) {
this.primary = primary;
}
@Override
public String toString() {
return "Person{"
+ "\n name=" + name
+ "\n address_line1=" + address_line1
+ "\n address_line2=" + address_line2
+ "\n address_city=" + address_city
+ "\n address_postcode=" + address_postcode
+ "\n address_state=" + address_state
+ "\n address_country=" + address_country
+ "\n primary=" + primary
+ "\n }";
}
}
ResponseWrapper
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class ResponseWrapper {
private ResponseObject response;
public ResponseObject getResponse() {
return response;
}
public void setResponse(ResponseObject response) {
this.response = response;
}
@Override
public String toString() {
return "ResponseWrapper{"
+ "\n response=" + response
+ "\n}";
}
}
TestResource
package jersey.stackoverflow.jaxrs;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/test")
public class TestResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response getResponse(ResponseObject ro) {
final String json = "{\n"
+ " \"response\": {\n"
+ " \"description\": \"test charge\",\n"
+ " \"email\": \"[email protected]\",\n"
+ " \"ip_address\": \"192.123.234.546\",\n"
+ " \"person\": {\n"
+ " \"name\": \"Matthew\",\n"
+ " \"address_line1\": \"42 Test St\",\n"
+ " \"address_line2\": \"\",\n"
+ " \"address_city\": \"Sydney\",\n"
+ " \"address_postcode\": \"2000\",\n"
+ " \"address_state\": \"WA\",\n"
+ " \"address_country\": \"Australia\",\n"
+ " \"primary\": null\n"
+ " }\n"
+ " }\n"
+ "}";
return Response.created(null).entity(json).build();
}
}
Unit Test: TestTestResource
import jersey.stackoverflow.jaxrs.ResponseWrapper;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.junit.Test;
public class TestTestResource extends JerseyTest {
@Test
public void testPostReturn() throws Exception {
final WebTarget target = target("test");
final ResponseWrapper ro = target.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(new ResponseWrapper()
, MediaType.APPLICATION_JSON_TYPE), ResponseWrapper.class);
System.out.println(ro);
}
@Override
protected Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
return createApp();
}
@Override
protected void configureClient(ClientConfig config) {
config.register(createMoxyJsonResolver());
}
public static ResourceConfig createApp() {
// package where resource classes are
return new ResourceConfig().
packages("jersey.stackoverflow.jaxrs").
register(createMoxyJsonResolver());
}
public static ContextResolver<MoxyJsonConfig> createMoxyJsonResolver() {
final MoxyJsonConfig moxyJsonConfig = new MoxyJsonConfig();
Map<String, String> namespacePrefixMapper = new HashMap<String, String>(1);
namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
moxyJsonConfig.setNamespacePrefixMapper(namespacePrefixMapper).setNamespaceSeparator(':');
return moxyJsonConfig.resolver();
}
}
Dependencies in pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>2.13</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-bundle</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
</dependencies>
Result: Just using toString
ResponseWrapper{
response=ResponseObject{
description=test charge
[email protected]
ip_address=192.123.234.546
person=Person{
name=Matthew
address_line1=42 Test St
address_line2=
address_city=Sydney
address_postcode=2000
address_state=WA
address_country=Australia
primary=null
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With