Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Apache CXF converts String to JSON incorrectly?

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?

like image 264
Slava Semushin Avatar asked May 13 '26 06:05

Slava Semushin


1 Answers

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.

Option 1: Use a Different Content Type

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();
    }
}

Option 2: Wrap the String in a JAXB-Annotated Object

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.

like image 118
Donal Fellows Avatar answered May 14 '26 23:05

Donal Fellows



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!