users@jersey.java.net

RE: [Jersey] json-from-jaxb example

From: Tyson Norris <tyson.norris_at_marketlive.com>
Date: Thu, 30 Oct 2008 12:29:50 -0700

 

 

 

Hi Paul -

I think yes you can say that - JSONJAXBContext ability to marshal
collections would help.

 

 

Actually this functionality is really associated with JAXB utilities
that use the abstraction of the StAX API. That way it does not have to
be specific to JSON. And, ideally this functionality should be part of
JAXB :-)

Agreed!

 

 





In the meantime, you gave me another idea, which is to build a class
similar to JSONListElementProvider; I'm not sure how to get a reference
to the JSONListElementProvier itself? If I find a way, I may try that as
well, and create an output stream adapter for a String.

 

 

Tis tricky because the Jersey infrastructure needs to be running to get
access. If you are running within Jersey you can inject the Providers
interface and select a message body writer.

I tried fiddling with this a bit, but using Type (or ParameterizedType,
for Lists) and OutputStream doesn't work well with code that is not
running within a Jersey request (although Jersey is running); I wanted
to use a variable to hold my List<MyType>, and simply pass it to the
writer and get the JSON string out of it.

 

        

        The method below gives me the functionality, BUT, there is an
odd problem in that if I enable the jxsw.writeEndElement(); call, I get
an array bounds exception:

        java.lang.ArrayIndexOutOfBoundsException: -1

                at java.util.ArrayList.get(ArrayList.java:324)

                at
com.sun.jersey.impl.json.writer.JsonXmlStreamWriter.writeEndElement(Json
XmlStreamWriter.java:222)

         

        I have simply commented out that line to get around the problem,
but I'm not sure of the ramifications...

 

It could be a bug due to a particular type of JAXB object. Does it work
when the collection of the same JAXB objects is returned from a resource
method?

Nope, it works great!

In fact, I got it to work as soon as I added:

marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);

 

 

So, my method for getting a Collection<?> (of JAXB annotated objects)
serialized to JSONObject is listed below, for benefit of others who may
want to do this.

 

One use case for this we ran into was when we tried to implement an AJAX
style file upload form that provides a JSON format response.

Form data is posted using a typical html form submission, but the form
is in a hidden iframe. (From what I have read, this is a typical
approach for AJAX style uploads, due to some restrictions the browser
has on accessing the file stream.)

 

Anyhow, the problem is with the response - since the browser gets a
response of type json/application, it pops open a text editor (or maybe
different if you have your browser set up differently). We need to
configure Jersey with @Produces application/json to get the response
into JSON format, but we need the browser to receive text/html or
text/plain type response header.

 So now we convert the response to JSON format but send it with a
text/plain type in the header.

 

There may be a more elegant way to implement this particular requirement
(response filter, etc), but we have a few other situations where we need
to serialize to JSON format, and the JsonXmlStreamWriter works great for
doing this.

 

As always, thanks for the feedback!

Tyson

 

 

 

    public JSONObject getJSONObject(Class<?> elementType, Collection<?>
t) throws JAXBException, JSONException

    {

        JAXBContext context = contextResolver.getContext(elementType);

        if (context == null){

            throw new JAXBException("no context resolved for:" +
elementType);

        }

        StringWriter jaxbStringWriter = new StringWriter();

        Marshaller marshaller = context.createMarshaller();

        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);

        

        // TODO: should reuse customization options from the marshaller
(if it is JSONMarshaller)

        // TODO: should force the elementType being treated as array
(for 1-elem lists)

        final XMLStreamWriter jxsw =
JsonXmlStreamWriter.createWriter(jaxbStringWriter, true);

        try {

            jxsw.writeStartElement(getRootElementName(elementType));

            for (Object o : t) {

                marshaller.marshal(o, jxsw);

            }

            jxsw.writeEndElement();

            jxsw.writeEndDocument();

            jxsw.flush();

        } catch (XMLStreamException ex) {

 
Logger.getLogger(JSONListElementProvider.class.getName()).log(Level.SEVE
RE, null, ex);

            throw new JAXBException(ex.getMessage(), ex);

        }

        return new JSONObject(jaxbStringWriter.toString());

    }

 

 

 

 

 

Paul.





Thanks!

Tyson

 

 

    public JSONObject getJSONObject(Class<?> elementType, Collection<?>
t) throws JAXBException, JSONException

    {

        JAXBContext context = contextResolver.getContext(elementType);

        if (context == null){

            throw new JAXBException("no context resolved for:" +
elementType);

        }

        StringWriter jaxbStringWriter = new StringWriter();

        Marshaller marshaller = context.createMarshaller();

        // TODO: should reuse customization options from the marshaller
(if it is JSONMarshaller)

        // TODO: should force the elementType being treated as array
(for 1-elem lists)

        final XMLStreamWriter jxsw =
JsonXmlStreamWriter.createWriter(jaxbStringWriter, true);

        try {

            jxsw.writeStartElement(getRootElementName(elementType));

            for (Object o : t) {

                    marshaller.marshal(o, jxsw);

            }

            //jxsw.writeEndElement();

            jxsw.writeEndDocument();

            jxsw.flush();

        } catch (XMLStreamException ex) {

 
Logger.getLogger(JSONListElementProvider.class.getName()).log(Level.SEVE
RE, null, ex);

            throw new JAXBException(ex.getMessage(), ex);

        }

        return new JSONObject(jaxbStringWriter.toString());

    }

 

 

 

________________________________

From: Paul.Sandoz_at_Sun.COM [mailto:Paul.Sandoz_at_Sun.COM]
Sent: Tuesday, October 28, 2008 9:54 AM
To: users_at_jersey.dev.java.net
Subject: Re: [Jersey] json-from-jaxb example

 

Hi Tyson,

 

Hmm... this is tricky. There is no explicit functionality to support
collections of JAXB beans. It is all encapsulated in the message body
readers/writers, which you can access via Providers [1]. But this
requires you have a Jersey set up running.

 

Would i be correct in saying that you want functionality on the
JSONJAXBContext for marshal/unmarshal collections?

 

Paul.

 

https://jsr311.dev.java.net/nonav/releases/1.0/javax/ws/rs/ext/Providers
.html

 

On Oct 28, 2008, at 5:36 PM, Tyson Norris wrote:






Thanks for the response Paul.

 

What we are doing, may be thought of as using the ContextResolver
outside the context of Jersey resource method calls. For example, I can
configure a ContextResolver as a spring bean, and then look up and use
that spring bean elsewhere.

 

This is a handy way to use a single approach (JAXB configs) to manage
conversion of POJO to JSON string. Some of these conversions are handled
by Jersey, some are manually invoked in application logic.

 

Jersey does this automatically for resource method signature types, and
Collections of JAXB classes work in that case. What I'm not sure how to
do, is make the ContextResolver work this way for Collections of JAXB
types that are NOT automatically handled by Jersey (since these
Collections of JAXB types are being used outside of any Jersey resource
methods). It works fine to use ContextResolver in this way for
non-Collections.

 

Another way to think of this situation is that we are trying to use some
of the Jersey infrastructure as a generic (read: used in cases outside
of Jersey resource method calls) way to generate JSON format strings
based on JAXB configuration.

 

Thanks!

Tyson

 

 

 

________________________________

From: Paul.Sandoz_at_Sun.COM [mailto:Paul.Sandoz_at_Sun.COM]
Sent: Tuesday, October 28, 2008 1:16 AM
To: users_at_jersey.dev.java.net
Subject: Re: [Jersey] json-from-jaxb example

 

Hi Tyson.

 

Collections of JAXB classes are supported.

 

See the JAXB sample for XML:

 

http://download.java.net/maven/2/com/sun/jersey/samples/jaxb/1.0/jaxb-1.
0-project.zip

 

Although this is for XML it should also work for JSON.

 

Paul.

 

 

On Oct 27, 2008, at 10:10 PM, Tyson Norris wrote:







Hi -

We have some JSON format services created, which are all working well,
based on the json-from-jaxb example.

 

Is there anyway to get the JSON format string of a Collection<?> type
using the ContextResolver injected via @Context? We can do it with JAXB
types, but I'm trying to figure out if we can avoid wrapping the
collections.

 

Background:

We have some occasions where we need to "manually" generate JSON format
strings and use a String type result on a resource method, which works
fine when we modify the types in our JAXBContextResolver class.

 

We would like to be able to also "manually" generate JSON format strings
from Collection<?> types, but I'm not sure if this is possible?

 

The method:

JAXBContext getContext(Class<?> objectType)

 

Only receives type info for the collection instance, not the type of its
contents.

 

 Any ideas on a way around this?

 

Our alternative will be to not rely on JAXB config to generate JSON
format in these cases, which is less than ideal.

 

Our use case for doing this is that we have some JSP templates that
serve HTML + javascript, and the javascript uses some AJAX style widgets
that consume JSON data. In some cases, it is best for us to serve all
the content at one time, where the JSON data is embedded into the HTML
as a static string in JSON format.

 

 

Thanks for any ideas!

 

Tyson