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).
The JAX-RS runtime will only look up one MessageBodyWriter
for the object returned by the resource method (see sectioin "4.2.2 Message Body Writer" in the specification), then that single MessageBodyWriter
has complete control over the serialization of the entire object graph to be returned to the client.
In order to implement the behavior you wanted, you would need a custom MessageBodyWriter
per media type, that is willing to delegate the serialization of a part of the object graph to another writer whenever it encounters a specific type in the graph, and then resume its own logic. Obtaining the delegate writer for the specific type wouldn't be a big problem (inject a javax.ws.rs.ext.Providers
and call getMessageBodyWriter()
), but I don't think the existing xml/json/etc serializers are implemented with such kind of extensions in mind, so I guess you couldn't just relay on them. Reimplementing an xml marshaller just for this is not an attractive option either.
Refer the below post for writing custom message body writer for your Java object's serialization.
http://h2labz.blogspot.in/2014/12/marshalling-java-to-json-in-jax-rs.html