Search code examples
springspring-mvcspring-4

Spring JSON Response: Serialize only the response object content (do not Wrap Root Value)


In my application every restful service returns the following object, which indicate whether the request terminated with success or an error, it contains an optional error message and, of course, the object:

public class JSONResponse {

    public boolean success = true;
    public String errmsg = "";
    public Object body;

    /* getter/setter */
}

When I call the restful service, I get the following JSON:

{
   "JSONResponse":{
      "success":true,
      "errmsg":"",
      "body":[]
    }
 }

Unfortunately, I already have the client-side code which expects to get only the object content as this:

{
   "success":true,
   "errmsg":"",
   "body":[]
}

I would like to preserve my existing JS code. How can I configure the MappingJackson2JsonView to get a JSON with only the content of the "JSONResponse:" field.

The following is part of a simplified restful controller:

@RequestMapping(value="/list")
public JSONResponse list() {
    JSONResponse response = new JSONResponse();
    response.success(new String[] { "a", "b", "c"});
    return response;
}//EndMethod.

In the xml I use the MappingJackson2JsonView default view as in the partial attached xml:

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="contentNegotiationManager" ref="contentNegotiationManager"/>
    <property name="viewResolvers">
        <list>
            <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
            <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                <property name="prefix" value="/WEB-INF/views/"/>
                <property name="suffix" value=".jsp"/>
            </bean>
        </list>
    </property>

    <property name="defaultViews">
        <list>
            <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />
        </list>
    </property>
</bean> 

Solution

  • In order to remove the root key name from the generated JSON response, I updated the spring config file by adding p:extractValueFromSingleKeyModel="true"

    <bean id="contentNegotiationManager"  class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">   
        <property name="favorPathExtension" value="true" />
        <property name="ignoreAcceptHeader" value="true"/>
        <property name="useJaf" value="false"/>
        <property name="defaultContentType" value="text/html" />
    
        <property name="mediaTypes">
            <map>
               <entry key="html" value="text/html"/>
               <entry key="json" value="application/json"/>
           </map>
        </property>
    </bean>
    
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="contentNegotiationManager" ref="contentNegotiationManager"/>
        <property name="viewResolvers">
            <list>
                <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
                <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                    <property name="prefix" value="/WEB-INF/views/"/>
                    <property name="suffix" value=".jsp"/>
                </bean>
            </list>
        </property>
    
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"
                    p:extractValueFromSingleKeyModel="true" />
            </list>
        </property>
    </bean>
    

    I also updated the root beans tag to provide "p" as in the following snippet:

    <?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:context="http://www.springframework.org/schema/context"
        xmlns:p="http://www.springframework.org/schema/p" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    

    A brief technical explanation

    Doing some tests with the debugger, I noticed that the method writeValue of the ObjectMapper receives an HashMap with only one item, where the key is "JSONResponse" and the value is the object returned by the controller method. Therefore, the ObjectMapper will generate the JSON from this HashMap instance, hence the presence of "JSONResponse" as root, because the serializer starts from the hash map object.

    By default the WRAP ROOT VALUE in spring has the value false, and does not affect the presence of "JSONResponse" string as root in the specific case. Indeed, by enabling it you will get { "HashMap": { "JSONResponse": ... }}. Therefore, the this.configure(SerializationFeature.WRAP_ROOT_VALUE, false) does not solve the issue.

    The MappingJackson2JsonView has a method setExtractValueFromSingleKeyModel, from the documentation here "Set whether to serialize models containing a single attribute as a map or whether to extract the single value from the model and serialize it directly". By default, it is false and does not extract the value from the hashmap, but the hashmap itself is the object converted to JSON. When this property is set to true, it extracts the value from the hash map and converts it in JSON.