users@jersey.java.net

Re: [Jersey] Polymorphic deserialization of JSON?

From: Chad McHenry <mchenryc_at_gmail.com>
Date: Sun, 10 May 2009 12:45:09 -0400

On Sat, May 9, 2009 at 10:43 PM, Craig McClanahan
<Craig.McClanahan_at_sun.com>wrote:

> Chad McHenry wrote:
>
> 2009/5/9 Felipe Gaúcho <fgaucho_at_gmail.com>
>
>> I am doing that.. with subclasses of JPA entities.. it works ..
>>
>> like that:
>>
>> http://fgaucho.dyndns.org:8080/footprint-service/user/read/2
>> http://fgaucho.dyndns.org:8080/footprint-service/user/edit/2
>>
>> notice the two service calls reads the same entity from the database,
>> but in one path I have a partial view on the entity, while in the
>> other path I have the complete representation...
>
>
> Thanks for sharing your code Filipe. As I feared, I'm looking for a
> solution to a different problem.
>
> Using shapes as an example, I would like to be able to send POST and PUT
> messages to a Jersey Resource which accepts a "Shape" as an argument, but
> instead of deserializing a Shape, I would like the unmarshaller to recognize
> the descriminator in the incoming data, and deserialize a Circle or
> Rectangle.
>
> So that I can POST both Circles and Rectangles to the same URL but the
> app will deserialize the correct subtype:
>
> This is what I would _like_ to see, but does not work yet.
>
> class Shape { public String shapeType; }
> class Circle extends Shape { public int radius; }
> class Rectangle extends Foo { public int width; public int height; }
>
> @Path("/shape")
> class ShapeResource {
> ...
> @POST
> public Foo create(Shape shape) {
> log.debug(shape.getClass());
> }
> ....
>
> curl -X POST -H 'Content-type: application/json' \
> -d '{"shapeType":"circle", "radius":33}' localhost/app/shape
> [debug] Circle
>
> curl -X POST -H 'Content-type: application/json' \
> -d '{"shapeType":"rectangle", "width":5, "height":3}
> ' localhost/app/shape
> [debug] Rectangle
>
> ====
>
> The issue I see is that by declaring a method that accepts a "Shape", the
> JSON unmarshaller looks only for the properties of shape and discards the
> interesting bits about rectangles and circles - which is why I can not use a
> JAXB @XMLAdapter annotation.
>
> I have considered separate URLs for each subtype, and may have to use
> them if I cannot find a way to unmarshal the subclasses.
>
> @POST
> @Path("/rectangle")
> public Foo createRectangle(Rectangle shape) {
> log.debug(shape.getClass());
> }
>
> @POST
> @Path("/circle")
> public Foo createCircle(Rectangle shape) {
> log.debug(shape.getClass());
> }
>
> curl -X POST -H 'Content-type: application/json' \
> -d '{"shapeType":"circle", "radius":33}' localhost/app/shape/circle
> [debug] Circle
>
> curl -X POST -H 'Content-type: application/json' \
> -d '{"shapeType":"rectangle", "width":5, "height":3}
> ' localhost/app/shape/rectangle
> [debug] Rectangle
>
> An alternative that will get you RESTful brownie points :-) would be
> to use a different media type for a Rectangle versus a Circle. RFC 4288[1]
> suggests using "application/vnd.xxxxxxx" for application specific media
> types, and coupling that with the notion of adding "+json" or "+xml" to the
> media type to indicate the syntax gives you, perhaps, something like this:
>

I hadn't considered using the media type, considering a Circle/Rectangle
different "object" types, of the same "media" type, but it is interesting
that we have at least 3 channels to use. I suppose using a custom HTML
header would be yet another:

curl -X POST -H 'Content-type: application/json' \
    -H 'X-com.example.app.shape: Circle' \
    -d '{"shapeType":"circle", "radius":33}' localhost/app/shape
[debug] Circle

curl -X POST -H 'Content-type: application/json' \
    -H 'X-com.example.app.shape: Rectangle' \
    -d '{"shapeType":"rectangle", "width":5, "height":3}
' localhost/app/shape
[debug] Rectangle

For my two cents, I think use of HTTP headers as the descriminator fall into
the category of "Just because you can, doesn't mean you should".



>
>
> @POST
> @Path("/shape")
> @Consumes("application/vnd.com.example.shapes.Rectangle+json")
> public Foo createRectangle(Rectangle shape) {
> ...
> }
>
> @POST
> @Path("/shape")
> @Consumes("application/vnd.com.example.shapes.Circle+json")
> public Foo createCircle(Circle shape) {
> ...
> }
>
> You can keep a discriminator in the representation if you want, but are not
> required to have one since the media type performs the same role.
>
> Craig McClanahan
>
> [1] http://tools.ietf.org/html/rfc4288
>
>
>
>
>
>> I am trying to finish the articles about that.. despoite the flu and
>> the time :) eheh
>>
>> Meanwhile, you can checkout my code from footprint:
>>
>> svn checkout https://footprint.dev.java.net/svn/footprint/trunk
>> footprint --username guest
>> cd footprint
>> mvn clean install
>>
>> and then you have the EAR file in the footprint-service/target
>> folder.. just start the database, the glassfish and then deploy this
>> ear....
>>
>> and keep asking here if you like the solution and if you have
>> questions about it......
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>
>>
>
>