1
votes

I have a jax-rs web services on jetty+jersey and tried different JSON message providers (e.g. Jackson and Gson). With all of them POJO <-> JSON works just fine but methods like this:

@GET
@Path("test")
@Produces(MediaType.APPLICATION_JSON)
public String test() {
    return "This string should be \"quoted\" by message writer";
}

produce string

This string should be "quoted" by message writer

which is of course not a valid JSON and causes browser's ajax calls to fail. I expect it to be

"This string should be \"quoted\" by message writer"

The reason for such magic is StringMessageProvider (one of the internal jersey providers for basic types) which have / Produce/Consume annotation. And I don't want to hack into jersey internal providers. I want to force jersey to use JSON provider in first place?

Any ideas?

2

2 Answers

2
votes

So, after reading jersey docs & some debuging it turned out that:

  • yes, custom message readers/writers have higher priority, but
  • after selecting "suitable" providers (starting from correct one), jersey sort them via custom comparation, that first checks 'type' distance

So, having method return type 'String' and JSON message provider MIME type wildcard (/), it checks first StringMessageProvider (default jersey provider for build-in type) by comparing distances String-String < String-Object.

The problem solved by adding non-generic custom message provider, e.g.:

public class StringProvider extends GsonProvider<String> {
    public StringProvider() {}
}

(where GsonProvider implements MessageBodyReader<T>, MessageBodyWriter<T>).

After this returned string was escaped to be a correct JSON and recognized by browsers' ajax call handlers.

1
votes

The JSON that you are generating is invalid (you may want to validate it with JSONLint).

So, a valid JSON can be:

{"msg":"This string should be \"quoted\" by message writer"}

["This string should be \"quoted\" by message writer"]

The Java code for each JSON:

@GET
@Path("test")
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> test() {
    Map<String, String> map = new HashMap<String, String>();
    map.put("msg", "This string should be \"quoted\" by message writer");
    return map;
}

@GET
@Path("test")
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> test() {
    return Arrays.asList("This string should be \"quoted\" by message writer");
}

NOTE: I'm using RESTEasy 3.0.8 with Jackson 2.4.1