Is is possible to specify the XML node names for marshalling/unmarshalling for as in Jersey

110 views
Skip to first unread message

masaharo

unread,
Sep 18, 2009, 5:40:53 AM9/18/09
to grails-jaxrs, matti...@sanoma.fi
I added an @xmlElement annotation into my domain object, but it does
not seem to have any effect on the resulting xml:


import javax.xml.bind.annotation.XmlElement;

class Note {

static constraints = {
}
@XmlElement(name="teksti")
String text
}

masaharo

unread,
Sep 18, 2009, 7:04:55 AM9/18/09
to grails-jaxrs
Tried even with XmlAccessType.NONE, with no success:


@XmlRootElement(name="nootti")
@XmlAccessorType( XmlAccessType.NONE )

Martin Krasser

unread,
Sep 18, 2009, 8:48:30 AM9/18/09
to grails-jaxrs
Let's assume you used a resource method implementation as described in
the docs... In this case Grails does the XML marshalling (... note as
XML... ) and the Grails XML marshalling mechanism ignores the XML
binding annotations. The result of 'note as XML' is a
grails.converters.XML object that is finally processed by a custom
MessageBodyWriter implemented by the plugin.

When you return the domain object instance directly e.g. with
'Response.ok(note).build()' and the domain object is properly JAXB-
annotated then the standard JAXB entity provider of Jersey is used.
For example a domain object

@XmlRootElement(name='abc')
@XmlAccessorType( XmlAccessType.NONE )
class Abc {

@XmlElement(name='xyz')
String xyz

}

and a JAX-RS resource

@Path('/test')
class TestResource {

@GET
@Produces('application/xml')
Response getTestRepresentation() {
return Response.ok(new Abc(xyz:'blah')).build()
}

}

yield the result

<abc>
<xyz>blah</xyz>
</abc>

Hope that helps

Cheers,
Martin

masaharo

unread,
Sep 21, 2009, 5:17:01 AM9/21/09
to grails-jaxrs
Thanks for help, it helped to make it work.I replaced the lines:

ok myNote

lines with

Response.ok(myNote).build()

In the scaffolded NoteResource.groovy

class, and it seems to work.

While getting a list of Note objects with the scaffolded
NoteCollectionResource.groovy, I ran into difficulties:

@GET
Response readAll() {
println "readAll"
//ok Note.findAll()
Response.ok(Note.findAll()).build()
}

I get an error:

org.codehaus.groovy.runtime.InvokerInvocationException:
java.lang.IllegalArgumentException: This method supports only the
Types Cookie, CacheControl, EntityTag, NewCookie and MediaType
at org.grails.jaxrs.web.JaxrsFilter.doFilterInternal(JaxrsFilter.java:
46)
Caused by: java.lang.IllegalArgumentException: This method supports
only the Types Cookie, CacheControl, EntityTag, NewCookie and
MediaType
at
org.restlet.ext.jaxrs.internal.spi.RuntimeDelegateImpl.createHeaderDelegate
(RuntimeDelegateImpl.java:101)
at com.sun.jersey.spi.container.ContainerResponse.getHeaderValue
(ContainerResponse.java:186)
at com.sun.jersey.spi.container.servlet.WebComponent
$Writer.writeStatusAndHeaders(WebComponent.java:213)
at com.sun.jersey.spi.container.ContainerResponse
$CommittingOutputStream.commitWrite(ContainerResponse.java:139)
at com.sun.jersey.spi.container.ContainerResponse
$CommittingOutputStream.write(ContainerResponse.java:113)

I guess that this has something to do with the fact that I have not
described anywhere how a collection of Note object should be
marshalled/unmarshalled? How could I do this in groovy? I tried with:

GenericEntity<List<Note>> entity = new GenericEntity<List<Note>>
(Note.findAll()) {};
Response response = Response.ok(entity).build();
Response.ok(response).build()

This just gave me an error message that there is no constructor for
GenericEntity with params Note and a groovy closure.

Any ideas?
Message has been deleted

masaharo

unread,
Sep 21, 2009, 7:50:12 AM9/21/09
to grails-jaxrs
I found out, that if I specify a custom entity handler:

@Provider
@Produces('text/xml')
class NoteListWriter extends MessageBodyWriterSupport<ArrayList>
{
void writeTo(ArrayList<Note> entityList, MultivaluedMap
httpHeaders, OutputStream entityStream) {
println "entityList=${entityList}"
if (entityList) {
def builder = new MarkupBuilder(new
OutputStreamWriter
(entityStream))
builder.notes {
for (Note entity: entityList) {
builder.note {
id(entity.id)
content(entity.text)
}

}
}
}
// Alternative (default rendering):
// entityStream << (entity as XML)
}

}

and save it into grails-app/providers, I can specify the custom XML
and use

Response.ok(Note.findAll()).build()

in the NoteCollectionResource class for listing all notes.

The problem now is, that I have to use a wide generics type ArrayList:

class NoteListWriter extends MessageBodyWriterSupport<ArrayList> {
void writeTo(ArrayList<Note> entityList, MultivaluedMap
httpHeaders, OutputStream entityStream) {
. . .

If I try to use <ArrayList<Note>>:

class NoteListWriter extends MessageBodyWriterSupport<ArrayList<Note>>
{
void writeTo(ArrayList<Note> entityList, MultivaluedMap
httpHeaders, OutputStream entityStream) {

, the message body writer is not
found:

SEVERE: A message body writer for Java type, class
java.util.ArrayList, and MIME media type, text/xml, was not found
Sep 21, 2009 1:59:05 PM
com.sun.jersey.server.impl.application.WebApplicationImpl onException
SEVERE: Internal server error
javax.ws.rs.WebApplicationException
at com.sun.jersey.spi.container.ContainerResponse.write
(ContainerResponse.java:241)

even if the call for the method is of type ArrayList<Note>.

Of cource I could implement one message body writer (e.g.
ArrayListWriter) for all ArrayList's and the have one writeTo() method
for each list element type.
This just doesn't feel the right solution. Maybe there is a better way
of doing it?

Martin Krasser

unread,
Sep 22, 2009, 1:00:16 AM9/22/09
to grails...@googlegroups.com
masaharo schrieb:
> While getting a list of Note objects with the scaffolded
> NoteCollectionResource.groovy, I ran into difficulties:
>
> @GET
> Response readAll() {
> println "readAll"
> //ok Note.findAll()
> Response.ok(Note.findAll()).build()
> }
>
> I get an error:
>
> ...
>
> I guess that this has something to do with the fact that I have not
> described anywhere how a collection of Note object should be
> marshalled/unmarshalled?
Yes that's the reason. You could create your own NoteList class here,
initialize it with all Note objects returned from Note.findAll(). When
the NoteList class has JAXB annotations, Jersey/Restlet will use the
standard JAXB provider to marshal the NoteList. Alternatively, you could
write your own provider (MessageBodyWriter) for NoteList to make a
custom XML marshalling e.g. with a Groovy builder.
> How could I do this in groovy? I tried with:
>
> GenericEntity<List<Note>> entity = new GenericEntity<List<Note>>
> (Note.findAll()) {};
> Response response = Response.ok(entity).build();
> Response.ok(response).build()
>
> This just gave me an error message that there is no constructor for
> GenericEntity with params Note and a groovy closure.
>
> Any ideas?
>
Here you make the assumption that the list returned by Grails implements
List<Note> which might be not the case. Even if this was the case you
need to have a provider for List<Note> in place to make it work.

masaharo

unread,
Sep 22, 2009, 1:46:06 AM9/22/09
to grails-jaxrs
OK. Thanks for these good answers! I think I'll go with the annotated
NoteList solution.

Cheers,
Matti

Martin Krasser

unread,
Sep 22, 2009, 3:23:25 AM9/22/09
to grails...@googlegroups.com
masaharo schrieb:
The functionality of MessageBodyWriterSupport in version 0.2 is limited
to simple use cases. It currently considers the raw type but not the
generic type. I opened an issue for that and address it in version 0.3

http://code.google.com/p/grails-jaxrs/issues/detail?id=6

In the meantime, you could implement javax.ws.rs.ext.MessageBodyWriter
directly and implement asWritable() accordingly. However, using generic
types like ArrayList<Note> for example, also requires to work with
javax.ws.rs.core.GenericEntity. As described in the Javadocs you
typically create an anonymous subclasses from it but this is not
possible in Groovy from what I know. Defining a named subclass on the
other hand is as much effort as writing a custom List implementation
e.g. NoteList.

If you have further requirements regarding generic types and provider
please add them to http://code.google.com/p/grails-jaxrs/issues/detail?id=6

masaharo

unread,
Sep 22, 2009, 6:10:48 AM9/22/09
to grails-jaxrs
OK,

thanks for answer. I created an empty class:

class NoteList extends ArrayList<Note> {
}

I didn't know how to annotate this class "Jersey-style" since it does
not have any class members in the source code.
But now I can use:

@Provider
@Produces('text/xml')
class NoteListWriter extends MessageBodyWriterSupport<NoteList> {
. . .

to produce listings of Note:s. Creating a subclass of the
ArrayLIst<Note> is maybe not a very elegant solution but works fine
for me.

-Matti
> please add them tohttp://code.google.com/p/grails-jaxrs/issues/detail?id=6

Martin Krasser

unread,
Sep 22, 2009, 7:25:06 AM9/22/09
to grails...@googlegroups.com
masaharo schrieb:
> OK,
>
> thanks for answer. I created an empty class:
>
> class NoteList extends ArrayList<Note> {
> }
>
> I didn't know how to annotate this class "Jersey-style" since it does
> not have any class members in the source code.
>
Although I didn't test it, you could try using a generic container (for
all domain object types) such as

@XmlRootElement
class DomainObjectContainer {
@XmlElement
List domainObjects;

...
}

(or XmlElementRef (?)) and have your domain objects annotated properly.
Then all XML marshalling should be handled by the default JAXB provider.
Reply all
Reply to author
Forward
0 new messages