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).
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 singleMessageBodyWriter
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 ajavax.ws.rs.ext.Providers
and callgetMessageBodyWriter()
), 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
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:the JAX-RS provider will serialize
Bar
using your custom writer. However for this resource method: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 forA
, and not for any of the types included inA
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:You tell Jackson to use this custom serializer thusly:
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
andjackson-jaxrs
jars in your classpath (and probably thejackson-core
one as well).