[求助] Spring4 MVC 返回json格式时候 设置不返回null值属性的问题

bighong0404 2015-10-06 12:43:40
背景:
使用@responseBody设置以json格式返回数据时候. 有时候被返回的对象有些属性是null值, 默认还是会输出. 例如下面代码. 在与移动端交互时候会很浪费流量.
{
"fpassword" : "sssssssss",
"favator" : "",
"fbirthday" : null,
"fcredType" : null,
"fcredid" : null,
"fregistedTime" : null,
"fstate" : 1,
"flstate" : 1,
"fstatusMask" : 0,
"fstatusMask1" : 0,
"fcreateTime" : 1443260277000,
"fmodifyTime" : 1443260277000,
"fstandby0" : null,
"fstandby1" : null,
"fstandby2" : null,
"fstandby3" : null,
"fstandby4" : null,
"fstandby5" : null,
"fstandby6" : null,
"fpassFlag" : 1,
"fquestion1" : null,
"fanswer1" : null,
"fquestion2" : null,
"fanswer2" : null,
"fregDeviceId" : null,
"fregClientIp" : null,
"fregChannel" : null,
"fpassModifyTime" : null
}
有两种方法设置不返回null值属性.
1. 在被返回的对象例如User类, 添加注解@JsonInclude(Include.NON_NULL)即可. 在spring4.1.6, jackson-databind 2.5.1版本亲测有效
spring使用的是fasterxml.jackson组件解析对象. 因此依赖一下包..
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>

问题来了!! 第二种方法:
2. spring mvc配置文件,
查看API文件, 发现com.fasterxml.jackson.databind.ObjectMapper有一下方法, 而JsonInclude.Include枚举类有个值: NON_NULL(感觉应该和方法1的注解是同一个),
 public ObjectMapper setSerializationInclusion(JsonInclude.Include incl) {
_serializationConfig = _serializationConfig.withSerializationInclusion(incl);
return this;
}

因此对jackson的objectMapper设置属性
<property name="serializationInclusion">
<!-- 注入枚举类型 -->
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
</property>



最后就是这样:
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="cacheSeconds" value="0" />
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<!-- <bean class="com.threeStepTech.ObjectMapper.CustomObjectMapper"/> -->
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
</bean>
</property>
<property name="serializationInclusion">
<!-- 注入枚举类型 -->
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
</property>
</bean>
</property>
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>


但测试过还是无效.....
也尝试了自己写子类继承com.fasterxml.jackson.databind.ObjectMapper.
public class CustomObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 3072523733092288622L;

public CustomObjectMapper() {
super.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
super.getSerializationConfig().withSerializationInclusion(
JsonInclude.Include.NON_NULL);
}
}

然后注入到上诉代码被注释的地方替换com.fasterxml.jackson.databind.ObjectMapper.. 还是无用....

求助哪位大神能帮忙解答一下.... 明显第二种方法优于第一种..... 但苦于无效...
...全文
19970 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
Ronrey 2018-10-12
  • 打赏
  • 举报
回复
除了牛逼我还能说什么呢,完美解决我的问题!
bighong0404 2015-10-07
  • 打赏
  • 举报
回复
这个问题已经找到原因了!!! 是controller方法之前接受参数使用String public Object getDefaultApprove(@RequestBody String jObj){ 改用一个Object去接收就ok了... public Object getDefaultApprove(@RequestBody JSONObject jObj){ 确实是json反序列化转对象. 但接口方法要求一个String... 类型不匹配所以报错了... 超级感谢.. 超级感谢
funnyone 2015-10-06
  • 打赏
  • 举报
回复
这个是反json化的错误,应该是json转化java对象,类型不匹配引起的。
bighong0404 2015-10-06
  • 打赏
  • 举报
回复
你好.... 我调了下位置.. 把 <mvc:annotation-driven /> 放到后面.. 然后报了这个错..
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: java.io.PushbackInputStream@3976ebfa; line: 1, column: 1]
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
	at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:835)
	at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:59)
	at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:12)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3562)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2662)
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:205)
	... 54 more
感觉应该和那个过滤null值属性Inclusion.NON_NULL的有关. 我把我的整个配置贴上来吧.. 不多.

<context:component-scan base-package="com.threeStepTech"/>
	
	<!-- 资源过滤 -->
	<mvc:resources mapping="/resources/**" location="/resources/" />
	
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		<property name="messageConverters">
			<list>
				<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
					<property name="objectMapper">
						<bean id="jacksonObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
							<property name="dateFormat">
								<bean class="java.text.SimpleDateFormat">
									<constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
								</bean>
							</property>
							<property name="serializationInclusion">
								<!-- 把枚举类型转为一个bean才能注入, 不序列化空值属性 -->
								<!-- <value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value> -->
								<util:constant static-field="com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL" />
							</property>
						</bean>						
					</property>
					<property name="supportedMediaTypes">
						<list>
							<value>application/json;charset=UTF-8</value>
						</list>
					</property>
				</bean>
				<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
					<property name="supportedMediaTypes">
						<list>
							<value>image/jpeg</value>
							<value>image/png</value>
							<!-- <value>application/json</value> -->
						</list>
					</property>
				</bean>
			</list>
		</property>
	</bean>

	<mvc:annotation-driven />

	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="utf-8"></property>
		<property name="maxUploadSize" value="10485760"></property>
		<property name="maxInMemorySize" value="40960"></property>
	</bean>

	<!-- 拦截器 -->
	<mvc:interceptors>
		<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
		<!-- sql注入检测 -->
		<bean class="com.threeStepTech.interceptor.SqlInjectInterceptor" />
		<!-- token检测 -->
		<bean class="com.threeStepTech.interceptor.TokenInterceptor" />
		<!-- 
		<mvc:interceptor>
			<mvc:mapping path="/test/number.do" />
			<!- - 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 - ->
			<bean class="com.host.app.web.interceptor.LoginInterceptor" />
		</mvc:interceptor>
		 -->
	</mvc:interceptors>  
	
	<!-- 切面 : 用户行为日志 -->
	<!-- <aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true"/> -->
	<aop:aspectj-autoproxy proxy-target-class="true" />

	<!-- spring socket -->
	<websocket:handlers>
        <websocket:mapping path="/myHandler" handler="myHandler"/>
        <websocket:handshake-interceptors>
            <bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
        </websocket:handshake-interceptors>
    </websocket:handlers>
    <bean id="myHandler" class="com.threeStepTech.springSkOfficial.MyHandler"/>
    	
   	
   	<import resource="classpath*:/server-config/spring-apphome.xml"/>
   	<import resource="classpath*:/server-config/spring-memcached.xml"/>
</beans>
超级感谢.....
funnyone 2015-10-06
  • 打赏
  • 举报
回复
我解释下为什么你写的无效?
<bean
      class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<.beans>
这样写当然可以,你的不行,是因为你把该bean定义放到<mvc:annotation-driven />之后了,<mvc:annotation-driven/>为生成一个RequestMappingHandlerAdapter的bean定义,只要把你的定义放到之前就可以了,注入时取先定义的。 这样写和mvc:message-converters的区别是: mvc:message-converters只会预先添加RequestMappingHandlerAdapter类属性messageConverters,而不会直接覆盖RequestMappingHandlerAdapter类。 代码见org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser.getMessageConverters(Element, Object, ParserContext):
private ManagedList<?> getMessageConverters(Element element, Object source, ParserContext parserContext) {
               //这里就是配置的bean,注意是放到前面的,因为解析时从前到后的
		Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
		ManagedList<? super Object> messageConverters = new ManagedList<Object>();
		if (convertersElement != null) {
			messageConverters.setSource(source);
			for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {
				Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);
				messageConverters.add(object);
			}
		}
             //这里是默认的return valuehandler
		if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {
			messageConverters.setSource(source);
			messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source));

			RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source);
			stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
			messageConverters.add(stringConverterDef);

			messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source));
			messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source));
			messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source));

			if (romePresent) {
				messageConverters.add(createConverterDefinition(AtomFeedHttpMessageConverter.class, source));
				messageConverters.add(createConverterDefinition(RssChannelHttpMessageConverter.class, source));
			}

			if (jackson2XmlPresent) {
				RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2XmlHttpMessageConverter.class, source);
				GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
				jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true);
				jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
				messageConverters.add(jacksonConverterDef);
			}
			else if (jaxb2Present) {
				messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
			}

			if (jackson2Present) {
				RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2HttpMessageConverter.class, source);
				GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
				jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
				messageConverters.add(jacksonConverterDef);
			}
			else if (gsonPresent) {
				messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source));
			}
		}
		return messageConverters;
	}
org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser.parse(Element, ParserContext)

ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
   //这里生成了RequestMappingHandlerAdapter bean
     RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
		handlerAdapterDef.setSource(source);
		handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
		handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
		handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
		addRequestBodyAdvice(handlerAdapterDef);
		addResponseBodyAdvice(handlerAdapterDef);
funnyone 2015-10-06
  • 打赏
  • 举报
回复
我改为jackson2.x的版本。 提交至spring mvc json custom example
bighong0404 2015-10-06
  • 打赏
  • 举报
回复
感谢楼上回复. .. 我用RequestMappingHandlerAdapter.. 而你用的是在mvc:annotation-driven设置message转换器.. 我水平浅还不清楚这两种写法的区别... 楼上的写法我也尝试过.. 百度过好多了.. 但也都没用... 另外感觉楼上的环境和我还不一样.. org.codehaus.jackson.map.ObjectMapper是jackson1.x时期的东西... 也不知道为毛我下了1.8.4的. 里面没有setSerializationInclusion()这个方法, 自然导致了报错... jackson2.x以后的包变成com.fasterxml.jackson路径....楼上你是什么环境的呢?
funnyone 2015-10-06
  • 打赏
  • 举报
回复
思路覆盖默认的MappingJacksonHttpMessageConverter。 重载MappingJacksonHttpMessageConverter,提供配置排除null值:
<mvc:annotation-driven>
		<mvc:message-converters>
			<bean
				class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
				<beans:property name="objectMapper">
					<beans:bean class="org.codehaus.jackson.map.ObjectMapper">
						<beans:property name="serializationInclusion">
							<util:constant
								static-field="org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_NULL" />
						</beans:property>
					</beans:bean>
				</beans:property>
			</bean>
		</mvc:message-converters>
	</mvc:annotation-driven>

81,092

社区成员

发帖
与我相关
我的任务
社区描述
Java Web 开发
社区管理员
  • Web 开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧