欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

jackson对json转化技巧

程序员文章站 2022-06-01 08:10:00
...

好多框架都使用jackson工具进行json转化,现介绍一些使用技巧,本篇使用的jackson版本是2.4.2

涉及到的jar包:cglib-nodep-2.2.jar,jackson-annotations-2.4.2.jar,jackson-core-2.4.2.jar,jackson-databind-2.4.2.jar

本文以数据服务的角度针对后台ElasticSearch和客户需求的场景进行描述


场景1:ElasticSearch查询返回的数据信息太多了,远超过接口需求文档中要返回的信息,如图:

jackson对json转化技巧

(buckets是个list,下面数据格式都相同,这里就不截图了)

而我方需求是:

jackson对json转化技巧

可以看出只是某些嵌套中少了几个属性,这时使用jackson的@JsonIgnoreProperties注释可以实现自动忽略功能:使用方法如下,只需在类上面加一行就行


@JsonIgnoreProperties(ignoreUnknown = true)
public class BucketsT2 {
    String key;
    int doc_count;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public int getDoc_count() {
        return doc_count;
    }

    public void setDoc_count(int doc_count) {
        this.doc_count = doc_count;
    }
}

采用这种方式只需按需求文档定义好反序列化模板,在进行转化时会自动忽略除了模板中的数据以外其他的数据

----------------------------------------------------------------------------------------------------------------------------------

场景2:Elasticsearch查询返回的数据中针对key:value的数据格式较多,有时会发现 有些key会是:make.keyword,color.keyword这种,而反序列化模板要以key为属性名,如图:

jackson对json转化技巧

这种样式的数据,java编写反序列化模板没法写,命名规则不允许有特殊字符,这就无法直接转换成对象(根本不能这么写)

jackson对json转化技巧


那除了手动各种readTree一层层通过角标遍历抽出这部分数据以外还有个解决办法:

使用jackon的@JsonProperty注释,作用是当遇到指定属性名时自动转化为反序列化模板中的属性

@JsonIgnoreProperties(ignoreUnknown = true)
public class BucketsT {
    String key_as_string;
   // long key;
 //   int doc_count;
    @JsonProperty("make.keyword")
    Make make;
    @JsonProperty("color.keyword")
    Make color;

这就会把数据中的mak.keyword转化为mak存起来,同理color.keyword也一样

转化后,通过debug查看反序列化结果:

jackson对json转化技巧

可以看到make.keyword已经存在了make中,这中方式就可以接收各种奇特的数据

--------------------------------------------------------------------------------------------------------------------------------

场景3:同样以ElasticSearch举例,一般json格式的反序列化模板,对于key:value是这么存的,key="",value="",这样存便于转化

jackson对json转化技巧

但如果需求文档要求返回的格式是(可能这种格式不是一个好的方案,但是解决方案是要有的)

jackson对json转化技巧

通过重写jackson Serializer过程,实现该功能,这里提供方法模板,可通过各种场景自定义具体处理逻辑:

首先我们要解决的问题:

1.参与序列化过程,让解析到该位置时,把red和1提取出来

2.动态生成一个类,加入red属性,赋值1

3.在序列化过程中返回新的类,即只替换了{key:red,doc_count:1}=>{read:1},其他整个json嵌套结构都没改变

(1)定义注释,定义序列化方法:

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


//JacksonAnnotationsInside用于创建注解,jasonSerialize用来指定序列化的类 
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@JacksonAnnotationsInside   
@JsonSerialize(using = TransformSerializer.class)
public @interface TransformField {
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import jackson_test.fieldmiss.object.BucketsT2;
import jackson_test.fieldmiss.object.DynamicBean;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;


public class TransformSerializer extends JsonSerializer<List<BucketsT2>> {
    @Override
    public void serialize(List<BucketsT2> old, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        //List<BucketsT2>表示我反序列化模板中的一个对象,如果需要修改String,int,或各种类型都可以,替换第一个参数的类型就行
       ArrayList<Object> list=new ArrayList<>();
        for(BucketsT2 temp:old){
            //获取key的值:red
            String key=temp.getKey();
            //获取count的值:1
            Integer value=temp.getDoc_count();
            //定义动态生成类的属性模板,map中存的数据格式为(属性名,属性类型的class)
            HashMap<String,Class> map=new HashMap<>();
            //(red,Integer.class)
            map.put(key,value.getClass());
            //传给对象生成器
            DynamicBean bean=new DynamicBean(map);
            //把属性值传递给生成器(red,1),会自动把1付给red属性
            bean.setValue(key,value);
            //getObject获得一个拥有一个read属性的类
            list.add(bean.getObject());
        }
        //替换了本来old数据的返回结果
        jsonGenerator.writeObject(list);

    }
}

(2)动态生成类

import net.sf.cglib.beans.BeanGenerator;
import net.sf.cglib.beans.BeanMap;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class DynamicBean {

    private  Object object = null;//动态生成的类
    private  BeanMap beanMap = null;//存放属性名称以及属性的类型

    public DynamicBean() {
        super();
    }

    @SuppressWarnings("rawtypes")
    public DynamicBean(Map propertyMap) {
        this.object = generateBean(propertyMap);
        this.beanMap = BeanMap.create(this.object);
    }

    /**
     * 给bean属性赋值
     * @param property 属性名
     * @param value 值
     */
    public void setValue(Object property, Object value) {
        beanMap.put(property, value);
    }

    /**
     * 通过属性名得到属性值
     * @param property 属性名
     * @return 值
     */
    public Object getValue(String property) {
        return beanMap.get(property);
    }

    /**
     * 得到该实体bean对象
     * @return
     */
    public Object getObject() {
        return this.object;
    }

    /**
     * @param propertyMap
     * @return
     */
    @SuppressWarnings("rawtypes")
    private Object generateBean(Map<String,Class> propertyMap) {
        BeanGenerator generator = new BeanGenerator();
        Set keySet = propertyMap.keySet();
        for (Iterator i = keySet.iterator(); i.hasNext();) {
            String key = (String) i.next();
            Class class_name=propertyMap.get(key);
            generator.addProperty(key,class_name);
        }
        return generator.create();
    }

}

最后的使用方法:

jackson对json转化技巧

只需在要转化的对象上加一个注解,这里注意buckets的类型一定要与Serializer相同



相关标签: jackson json