Reputation: 15204
I faced with unexpected Apache CXF behavior: when I return String
from my service it doesn't converted properly to JSON -- instead of adding double quotes around the string, Apache CXF returns it as is.
My service:
@Path(ROOT_URL)
@Produces(MediaType.APPLICATION_JSON)
public class SomeRestService {
@GET
@Path(SERVICE_URL)
public Response getString() {
return Response.ok("OK").build();
}
}
Part of Apache CXF configuration:
<jaxrs:server id="RestService" address="/api">
<jaxrs:serviceBeans>
<bean class="org.example.project.ws.rest.SomeRestService" />
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
</jaxrs:extensionMappings>
</jaxrs:server>
Can you help me to understand why this is working that way and how to tell to CXF return string as really JSON?
Upvotes: 3
Views: 4405
Reputation: 137567
JSON is a way of serializing complex objects, which Java's strings are (effectively) not. This means that there is no way to directly serialize a string into a JSON object; the serializer just doesn't know what to map it as and so bails out (or rather it doesn't enable itself for String
). There are two possible fixes for this.
The natural mapping for a string is to message content of type text/plain
, not application/json
. This mapping is built into CXF (and any other JAX-RS implementation too) and it's really easy to make it work. (I prefer to put produces and consumes annotations on individual methods rather than an overall class, but that's personal choice.)
@Path(ROOT_URL)
public class SomeRestService {
@GET
@Path(SERVICE_URL)
@Produces("text/plain")
public Response getString() {
return Response.ok("OK").build();
}
}
If you really want the data to go as JSON, you need to provide additional metadata. The overwhelmingly-simplest way of doing this by adding a wrapper class that has JAXB annotations, which then allows Jettison (the serialization library used by CXF) to spit the right thing out. In the sample below, I am using a static inner class, because it keeps a (fundamentally boring) class close to the only place that uses it, but YMMV; it does have to be an annotated public “struct” though (i.e., a class with a no-arg constructor and either public fields or suitably trivial getters and setters).
@Path(ROOT_URL)
public class SomeRestService {
@GET
@Path(SERVICE_URL)
@Produces("application/json")
public Response getServiceMessage() {
ServiceMessage result = new ServiceMessage();
result.message = "OK";
return Response.ok(result).build();
// Or: return Response.ok(result,"application/json").build();
// Or: return Response.ok(result).type("application/json").build();
}
@XmlRootElement // This annotation _is_ required, can be customized further
public static class ServiceMessage {
@XmlElement // This annotation isn't required, but documents intention
public String message;
}
}
I've omitted building out a full set of constructors for ServiceMessage
, which makes using it slightly less convenient than it might be; the code is (almost) purely bare bones minimal.
Upvotes: 1