问题
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>
回答1:
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.
回答2:
Define your own ObjectMapper
like this:
public class MyCustomMapper extends ObjectMapper {
public MyCustomMapper() {
//this.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, false);
//this.setSerializationInclusion(Include.NON_NULL);
this.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
}
}
Then add the bean definition in your xml:
<bean id="myCustomObjectMapper" class="put.your.package.name.MyCustomMapper"/>
Finally reconfigure the MappingJackson2HttpMessageConverter
:
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="myCustomObjectMapper" />
</bean>
来源:https://stackoverflow.com/questions/38777358/spring-json-response-serialize-only-the-response-object-content-do-not-wrap-ro