7

I have a restful web service, and the response is:

{
    "cities": [{
        "id": "1",
        "name": "City 01",
        "state": "A1"
    }, {
        "id": "2",
        "name": "City 02",
        "state": "A1"
    }]
}

But I want this:

{
    [{
        "id": "1",
        "name": "City 01",
        "state": "A1"
    }, {
        "id": "2",
        "name": "City 02",
        "state": "A1"
    }]
}

How I can configure JAX-RS to produces JSON without root node using only JAX-RS feature, and not implementation specific feature? My code needs to be portable across any appserver.

Navnath Godse
  • 2,233
  • 2
  • 23
  • 32
Otávio Garcia
  • 1,372
  • 1
  • 15
  • 27

3 Answers3

4

I had the same problem with Glassfish v3. I found this behavior depends on the JAX-RS implementation and switching to Codehaus' Jackson JAX-RS implementation solved the problem for me.

If you're using Glassfish as well, then you can solve the problem by adding org.codehaus.jackson.jaxrs to your war as well as to the WEB-INF/web.xml configuration as follows:

<!-- REST -->

<servlet>
  <servlet-name>RESTful Services</servlet-name>
  <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
    <param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>you.service.packages;org.codehaus.jackson.jaxrs</param-value>
    <!-- NOTE: The last element above, org.codehaus.jackson.jaxrs, replaces the default
       JAX-RS processor with the Codehaus Jackson JAX-RS implementation. The default
       JAX-RS processor returns top-level arrays encapsulated as child elements of a
       single JSON object, whereas the Jackson JAX-RS implementation return an array.
    -->
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>RESTful Services</servlet-name>
  <url-pattern>/your/rest/path/*</url-pattern>
</servlet-mapping>

Alternatively, you might be able to simply intercept the response in the client:

function consumesCity(json) {
   ...
}

Replace

... consumesCity(json) ...

with

function preprocess(json) {
    return json.city;
}

... consumesCity(preprocess(json)) ...
Kim Burgaard
  • 3,508
  • 18
  • 11
  • 1
    Thank you, Kim. But I'll still looking for a portable solution. – Otávio Garcia Jan 30 '11 at 04:28
  • 3
    I got the impression this particular behavior is vendor-specific, i.e. whether the top level JSON object for a collection return type will be an array, or a single object with a field that contains the collection members. I was a bit dismayed to see that Glassfish's reference implementation returns the latter style for collections. I think returning a JSON array is much more intuitive. Please post an update if you find a better solution. – Kim Burgaard Jan 30 '11 at 05:14
  • 1
    I agree with Kim. While structures do indeed differ -- mostly due to historical reasons, and specifically because there are libs that handle JSON via XML APIs, which sometimes forces use of wrappers -- there isn't really a more standard way, unless you want to use custom MessageBodyWriter/Reader implementations. – StaxMan Feb 01 '11 at 05:28
1

Good question. I have had an requirement similar to this. I had to have access to the generated raw response and do some manipulation. I achieved that by registering a resonse filter and then adapt a custom reponsewriter. See link below for more details.

http://www.mentby.com/paul-sandoz/access-to-raw-xml-in-jersey.html

In your response filter, you could clip out the class name from generated json, or better yet, return String in response and use custom json serialization mechanism like Google-gson.

Let me know if this solution works.

uncaught_exceptions
  • 21,712
  • 4
  • 41
  • 48
0

Answer from Kim Burgaard above works also for Jersey Spring WS. I had the same problem using Glassfish 3.0 and solved it adding the parameter shown below.

Example web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>Jersey Spring Web Application</servlet-name>
        <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>org.codehaus.jackson.jaxrs</param-value>
<!--             NOTE: The last element above, org.codehaus.jackson.jaxrs, replaces the default
               JAX-RS processor with the Codehaus Jackson JAX-RS implementation. The default
               JAX-RS processor returns top-level arrays encapsulated as child elements of a
               single JSON object, whereas the Jackson JAX-RS implementation return an array.-->
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Spring Web Application</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>
</web-app>

Example applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
            http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    <!--  Scan for both Jersey Rest Annotations and persistence classes  -->
    <context:component-scan base-package="your.service.packages"/>
</beans>
Juangui Jordán
  • 6,091
  • 2
  • 35
  • 31