Say I have a data class in a JAX-RS 1 environment (RestEasy 2 with the Jackson provider) like this:
class Foo {
int id;
String name;
Bar bar;
...
}
with Bar
being:
class Bar {
int one;
String two;
}
Now I want to have Bar
serialized in a special way (perhaps depending on the media type that was requested (or depending the phase of the moon), I would write a MessageBodyWriter<Bar>
@Provider
@Produces("application/json")
public class BarWriter implements MessageBodyWriter<Bar> {
...
}
which works very well if Bar
is requested on its own like in
@GET @Path("bar")
public Bar getBar() { return new Bar(...); }
But when I request Foo
as in
@GET @Path("foo")
public Foo getFoo() { return new Foo(...); }
the message body writer is ignored.
Now what I want is that this MessageBodyWriter
is also used when I return Foo
or a List<Bar>
I think the latter can be achieved by just writing a custom MessageBodyWriter
for the List
case, but for the former case I can't write a message body writer for all my application classes that contain a Bar
field.
Any ideas on how to solve this? I was also trying to use a Jackson serializer on the Bar
instance, but it looks like this is not even registered by RestEasy (and then, I think that way is too fragile anyway).
Unfortunately, this is not how message body writers work. The JAX-RS implementation will locate a writer, to be used in serialization, based on the type being returned from your resource method. So in your case, with a custom writer defined for Bar
, with this resource method:
@GET @Path("bar")
public Bar getBar() { return new Bar(...); }
the JAX-RS provider will serialize Bar
using your custom writer. However for this resource method:
@GET @Path("foo")
public Foo getFoo() { return new Foo(...); }
you do not have a custom writer defined, and serialization will be handled by the first matching (default) provider that can handle the combination of return class and content-type. A key thing to remember is that, unlike typical JSON and XML serialization libraries, JAX-RS entity providers are not recursive. Aka, for a given object A
being returned in a resource method, the provider will attempt to locate a custom writer only for A
, and not for any of the types included in A
as variables.
Since you are using Jackson though, why not just define a custom serializer for your Bar
class? That will handle pretty much every scenario you described:
public class BarSerializer extends JsonSerializer<Bar> {
@Override
public void serialize(final Bar value, final JsonGenerator jgen,
final SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeStartObject();
jgen.writeFieldName("myBar");
jgen.writeString(value.getTwo());
jgen.writeEndObject();
}
}
You tell Jackson to use this custom serializer thusly:
@JsonSerialize(using=BarSerializer.class)
class Bar {
int one;
String two;
}
Lastly, don't forget that if you anticipate getting JSON back in the same form as you serialized, that you will also need a custom JsonDeserializer
.
To get it to work, you need the jackson-mapper
and jackson-jaxrs
jars in your classpath (and probably the jackson-core
one as well).
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