PentaKon
PentaKon

Reputation: 4636

Jersey custom provider throws IOException for UTF-8 encoding

I have created a custom provider that serializes my objects into JSON because the default Jackson serialization seemed to not work properly when using UTF-8 encoding (it was adding extra illegal characters that could not be decoded by the client).

Provider:

@Provider
@Produces(MediaType.APPLICATION_JSON+"; charset=UTF-8")
public class JsonSerializer implements MessageBodyWriter<ResponseModel> {

    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public long getSize(ResponseModel t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return 0;
    }

    @Override
    public void writeTo(ResponseModel entity, Class<?> type, Type genericType, 
            Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, 
            OutputStream entityStream) throws IOException, WebApplicationException {        
        Gson gson = new GsonBuilder().setDateFormat("dd/MM/yyyy HH:mm:ss").create();
        JSONObject output = new JSONObject();
        output = (JSONObject) JSONValue.parse(gson.toJson(entity));
        entityStream.write(output.toString().getBytes());
        entityStream.flush();
    }
}

Here is my jersey servlet web.xml:

<servlet>
        <servlet-name>ServletAdaptor</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <!-- Register resources and providers under com.vogella.jersey.first package. -->
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>gr.modus.ext</param-value>
        </init-param>
        <!--init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
    </init-param-->
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>gr.modus.ext</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

Here is a sample logic from a web service method:

    ResponseModel<AnnouncementStatus> rm = new ResponseModel<AnnouncementStatus>();
    rm.setData(AnnouncementUtils.getStatuses(userId));
    rm.setSuccess(true);
    return Response.ok(rm).build();

Whenever I call this method, it goes through my JsonSerializer correctly but it gives the following internal server error:

java.nio.charset.MalformedInputException: Input length = 1 at java.nio.charset.CoderResult.throwException(CoderResult.java:260) at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:781) at com.sun.faces.application.ByteArrayWebOutputStream.writeTo(ByteArrayWebOutputStream.java:112) at com.sun.faces.application.ViewHandlerResponseWrapper.flushToWriter(ViewHandlerResponseWrapper.java:162)

The weird thing is that everything works correctly when my annotation produces just MediaType.APPLICATION_JSON. However the response data isn't correctly encoded so I was forced to add the encoding manually, and now I have this problem...

Upvotes: 0

Views: 329

Answers (1)

artbristol
artbristol

Reputation: 32397

entityStream.write(output.toString().getBytes()) is wrong. You should use getBytes(StandardCharsets.UTF_8) (although it seems a roundabout way of getting there, even then. Can't you use write?)

JSON is always serialised with UTF-8, so there is no need to add "; charset=UTF-8" to the content-type.

Upvotes: 1

Related Questions