users@jersey.java.net

Re: [Jersey] json-from-jaxb example

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Fri, 31 Oct 2008 08:38:01 +0100

On Oct 30, 2008, at 8:29 PM, Tyson Norris wrote:
> 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.

Have you seen:

https://jsr311.dev.java.net/nonav/releases/1.0/javax/ws/rs/core/GenericEntity.html

which makes it easier to obtain the parameterized type.


>
>> 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(JsonXmlStreamWriter.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);
>

Ah!


>
> 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.
>

Thanks.


> 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.

Oh gosh, well i suppose that "text/plain" is still technically correct.


> 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.
>

Great.

BTW JsonXmlStreamWriter is not currently a public part of the Jersey
API. It may be useful to make it so, but i think we need to clean and
improve the StAX abstraction a little bit.

Paul.

> 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.SEVERE, 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.SEVERE, 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
>
>
>