Spring应用之Jackson

加入依赖包:

<dependency>
  <groupId>com.fasterxml.jackson.jaxrs</groupId>
  <artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>

动态过滤属性

问题描述

ISSUE:双向关联对象转JSON问题 双向关联后使用org.codehaus.jackson.map.ObjectMapper转json时报错(由于两边都关联,会形成死循环)

例如:父子关系引用

Table Class Relation
user(name,createDate) user(name,createDate,articles) oneToMany (One User can post many Articles)
article(title) article(title,user) ManyToOne (one Article can be post by one User)

=> 直接输出肯定是报循环错误

解决方案

方式一:在类属性上加入注解@JsonManagedReference,@JsonBackReference

  • User类中(父):
    @JsonManagedReference
    public Set<Article> getArticles() {
      return articles;
    }
    
  • Article类中(子):
    @JsonBackReference
    public User getUser() {
        return user;
    }
    
  • Result:
    UserJson {"name":"chris","createDate":"2012-04-18","articles":[{"title":"title"}]}
    ArticleJson {"title":"title"}
    

方式二:加在类属性上加入注解@JsonIgnore

  • User类中(父):
    @JsonIgnore
    public Set<ArticleB> getArticles() { 
         return articles; 
    }
    
  • Result:
    UserJson {"name":"chris","createDate":"2014-07-23 11:03:44"}
    ArticleJson {"title":"title","user":{"name":"chris","createDate":"2014-07-23 11:03:44"}}
    

方式三: 在类上加入注解@JsonIgnoreProperties

  • User类上(父):
    @JsonIgnoreProperties("articles")
    public class User{...}
    
  • Result(同方式二)

方式四:@JsonFilter + Jacksons filter

  • User类上:
    @JsonFilter ("myFilter" )
    public class User{...}
    
  • 测试:
    String userJson = Jacksons.me().filter("myFilter" , "articles" ).readAsString(user);
    
  • Result:
    UserJson {"name":"chris","createDate":"2014-07-23 11:08:46"}
    
  • 注意:
    • 如果用另外一个没有添加该filter的ObjectMapper解析的话会报错
    • 如果这个User类已经添加了@JsonFilter("myFilter")注解,但在另外一个地方又要解析它并不想过滤name 属性,
    • 那只能是Jacksons.me().filter("myFilter", ""),然后再读出来

方式五:@JsonIgnoreProperties +Jacksons addMixInAnnotations

  • 添加过滤类:
    @JsonIgnoreProperties ("articles" )
    public interface MixInUser{}      // class 或interface都可
    
  • 测试:
    String mixInUser = Jacksons.me().addMixInAnnotations(User. class , MixInUser.class ).readAsString(user);
    
  • Result:
    UserJson {"name":"chris","createDate":"2014-07-23 11:10:43"}
    

单向过滤

使用@JsonIgnore+@JsonProperty

应用: JsonDeserialize时过滤某属性,JsonSerialize时保留属性

@JsonIgnore
private Date createDate;
@JsonProperty
public Date getCreateDate(){
    return this.createDate;
}
@JsonIgnore
public void setCreateDate(Date createDate){
    this.createDate=createDate;
}

动态解析

  • @JsonSerialize : 序列化 Object to JsonString
  • @JsonDeserialize:反序列化 JsonString to Object

使用举例:

  1. POJO:
     @Column(name = "evalContent", nullable = false)
     @Lob
     @JsonSerialize(using=CustomContentSerialize.class)
     public byte[] getEvalContent(){
         return this.evalContent;
     }
     @JsonDeserialize(using=CustomContentDeserialize.class)
     public void setEvalContent(byte[] evalContent){
         this.evalContent = evalContent;
     }
    
  2. Serialize:Read Object to JsonString(CustomContentSerialize: byte[] to String)
     public class CustomContentSerialize extends JsonSerializer<byte[]> {
         @Override
         public void serialize(byte[] value, JsonGenerator jgen,
                 SerializerProvider provider) throws IOException,
                 JsonProcessingException
         {
             System.out.println("CustomContentSerialize------------");
             String content=new String(value,"UTF-8");
             /*System.out.println(content);*/
             jgen.writeString(content);
         }  
     }
    
  3. Deserialize:Read JsonString to Object (CustomContentDeserialize: String to byte[])
     public class CustomContentDeserialize extends JsonDeserializer<byte[]> {
         @Override
         public byte[] deserialize(JsonParser jp, DeserializationContext ctxt)
                 throws IOException, JsonProcessingException
         {
             //System.out.println("CustomContentDeserialize-----------");
             String content=jp.getText();
             //System.out.println(content);
             return content.getBytes("UTF-8");
         }
     }
    

SpringMVC中扩展应用

  1. 自定义ObjectMapper

    • 序列化时自动转换时间格式为yyyy-MM-dd HH:mm:ss
    • 序列化时对一些特殊字符进行转换(为防止Xss恶意攻击)

      public class MyDateObjectMapper extends ObjectMapper implements InitializingBean  {
        private static final long serialVersionUID = 293736193275041317L;
        public MyDateObjectMapper() {
            _serializationConfig = _serializationConfig.with(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
            this.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
            this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        }
      
        private boolean doHtmlXssSerializer=false;
        public boolean isDoHtmlXssSerializer(){
            return doHtmlXssSerializer;
        }
        public void setDoHtmlXssSerializer(boolean doHtmlXssSerializer){
            this.doHtmlXssSerializer = doHtmlXssSerializer;
        }
        @Override
        public void afterPropertiesSet() throws Exception{
            System.out.println("doHtmlXssSerializer-----------");
            if(doHtmlXssSerializer){
                 SimpleModule module = new SimpleModule("HTML XSS Serializer");
                 module.addSerializer(new JsonHtmlXssSerializer(String.class));
                 this.registerModule(module);
            }
        }
      }
      
      public class JsonHtmlXssSerializer extends StdSerializer<String>{
        protected JsonHtmlXssSerializer(Class<String> t){
            super(t);
        }
        @Override
        public void serialize(String value, JsonGenerator jgen,SerializerProvider provider) throws IOException,JsonProcessingException{
            if (value != null) {
                 String encodedValue=value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
                 System.out.println("encodedValue:"+encodedValue);
                 jgen.writeString(encodedValue);
              }
        }
      }
      
  2. Spring MVC 中注入自定义的ObjectMapper (配置xxx-servlet.xml

     <!-- Jackson ObjectMapper -->
     <bean id="myObjectMapper" class="com.cj.support.jackson.MyDateObjectMapper">
         <property name="serializationInclusion">
              <value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
          </property>
          <property name="doHtmlXssSerializer" value="false"/>
     </bean>
    
     <mvc:annotation-driven>
       <mvc:message-converters>
             <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                 <!-- 这里注入自定义的ObjectMapper -->
                  <property name="objectMapper" ref="myObjectMapper"/>
             </bean>
         </mvc:message-converters>
     </mvc:annotation-driven>
    
     <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
         <property name="mediaTypes">
             <map>
                 <entry key="json" value="application/json"/>
             </map>
         </property>
         <property name="viewResolvers">
             <list>
                 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                     <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
                     <property name="prefix" value="/WEB-INF/"/>
                     <property name="suffix" value=".jsp"/>
                 </bean> 
             </list>
         </property>
         <property name="defaultViews">
             <list>
                 <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" >
                     <!-- 这里注入自定义的ObjectMapper -->
                     <property name="objectMapper" ref="myObjectMapper" />
                 </bean>
             </list>
         </property>
     </bean>