jackson的readValue和convertValue方法

jackson是一个强大的json工具库,但api不够直观(至少不如fastJson),使用起来总是没有那么友好。本文介绍readValue和convertValue这两个方法的使用和区别。

在使用jackson对json处理之前, 首先要创建ObjectMapper对象:

ObjectMapper objectMapper = new ObjectMapper()

说明:这个对象是线程安全的,在网上看到过,在高并发环境下,为了保证线程安全会有较高的锁竞争,所以很多时候都是每次通过new来创建ObjectMapper

一、readValue()方法:

这个方法有很多个重载,但是总的来说都是用来将json字符串,转换成一个object(bean、map、List<bean/map>)。

先来看一些准备数据:

@Data
public class User {
	private String name;
	private String nameEn;
}

//简单类型
String arrayStr = "[{\"name\":\"Zhangsan\"},{\"nameEn\":\"Lisi\"}]";
String jsonStr = "{\"name\":\"a\",\"nameEn\":\"b\"}";

//复杂类型
private static String getComplex1() throws Exception{
    List<Map<String,User>> list = new ArrayList<>();
    Map<String,User> m1 = new HashMap<>();
    m1.put("1", new User("zs","zs_en"));
    Map<String,User> m2 = new HashMap<>();
    m2.put("3", new User("ls","ls_en"));
    list.add(m1);
    list.add(m2);
    
    return new ObjectMapper().writeValueAsString(list);
}
private static String getComplex2() throws Exception{
    Map<String,List<User>> m = new HashMap<>();
    List<User> list1 = new ArrayList<>();
    list1.add(new User("tt","tt1"));
    m.put("1", list1);
    
    List<User> list2 = new ArrayList<>();
    list2.add(new User("ww","ww1"));
    m.put("2", list2);
    
    return new ObjectMapper().writeValueAsString(m);
}

readValue支持一下三种api:

  • readValue(String content, Class<T>  valueType)

  • readValue(String content, TypeReference<T> valueTypeRef)

  • readValue(String content, JavaType valueType)

1、readValue(String content, Class<T>  valueType)使用:

/**
* readValue(String content, Class<T>  valueType)
*/

//bean
User uu = new ObjectMapper().readValue(jsonStr, User.class);
System.out.println(uu);//User(name=a, nameEn=b)
//map
Map mm = new ObjectMapper().readValue(jsonStr, Map.class);
System.out.println(mm);//{name=a, nameEn=b}

//默认jackson将每个json对象封装成LinkedHashMap,然后放到list中
List<Map> readValue = new ObjectMapper().readValue(arrayStr, List.class);
System.out.println(readValue);//[{name=Zhangsan}, {nameEn=Lisi}]

//List<bean> 无法构造

//复杂类型
String complexStr1 = getComplex1(); //List<Map<String, User>>
String complexStr2 = getComplex2(); //Map<String,List<User>>

List<Map> list1 = new ObjectMapper().readValue(complexStr1, List.class);
System.out.println(list1); //[{1={name=zs, nameEn=zs_en}}, {3={name=ls, nameEn=ls_en}}]

Map map1 = new ObjectMapper().readValue(complexStr2, Map.class);
System.out.println(map1); //{1=[{name=tt, nameEn=tt1}], 2=[{name=ww, nameEn=ww1}]}

说明:该api主要是用来将json字符串装成bean或者map,对于json数组的情况,默认是转成LinkedHashMap放到list中(无法指定list元素类型),所以对于List<Bean>这种结构,推荐使用下面两种方式。

2、readValue(String content, TypeReference valueTypeRef)使用:

TypeReference可以用来指定反序列化时数据类型,支持json对象和json数组。

示例:

/**
 * readValue(String content, TypeReference valueTypeRef) 
 */
private static  void typeReference() throws Exception {
    //bean
    User uu1 = new ObjectMapper().readValue(jsonStr, new TypeReference<User>(){});
    System.out.println(uu1); //User(name=a, nameEn=b)
    //map
    Map mm1 = new ObjectMapper().readValue(jsonStr, new TypeReference<Map>(){});
    System.out.println(mm1); //{name=a, nameEn=b}
    
    //list<bean/map>
    List<User> userList = new ObjectMapper().readValue(arrayStr, new TypeReference<List<User>>(){});
    System.out.println(userList); //[User(name=Zhangsan, nameEn=null), User(name=null, nameEn=Lisi)]
    
    List<Map> mapList = new ObjectMapper().readValue(arrayStr, new TypeReference<List<Map>>(){});
    System.out.println(mapList); //[{name=Zhangsan}, {nameEn=Lisi}]
    
    //复杂类型
    String complexStr1 = getComplex1(); //List<Map<String, User>>
    String complexStr2 = getComplex2(); //Map<String,List<User>>
    
    List<Map<String, User>> list1 = new ObjectMapper().readValue(complexStr1, new TypeReference<List<Map<String, User>>>(){});
    System.out.println(list1); //[{1=User(name=zs, nameEn=zs_en)}, {3=User(name=ls, nameEn=ls_en)}]
    Map<String, List<User>> map1 = new ObjectMapper().readValue(complexStr2, new TypeReference<Map<String,List<User>>>(){});
    System.out.println(map1); //{1=[User(name=tt, nameEn=tt1)], 2=[User(name=ww, nameEn=ww1)]}
}

说明:该api可以将json对象和json数组的字符串转成对应的bean/map 和 List<bean/map>。

3、readValue(String content, JavaType valueType)使用:

利用 TypeFactory.constructParametricType()进行JavaType的类型构造

3.1)通过TypeFactory构造不同的JavaType:

1)constructMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass):

转换成map,同时指定key和value类型;

注:无法通过TypeFactory直接构造出一个Bean

2)constructCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass):

转成成list,并指定元素类型;

3)constructParametricType:

这个方法可以处理各种复杂结构,主要有以下两个声明:

public JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses) 
public JavaType constructParametricType(Class<?> rawType, JavaType... parameterTypes)

方法中后面的可变形参参数必须一致。JavaType可以通过调用getRawClass方法来变成Class类型。对于复杂结构,核心思路:从内到外构造类型,从右到左,一步一步来!

3.2)示例:

/**
     * readValue(String content, JavaType valueType) 
     */
    private static void javaTypeTest() throws Exception {
        System.out.println("--------JavaType---------");
        //bean 无法构造
        
        //map
        Map<String,String> mm = new ObjectMapper().readValue(jsonStr, 
                new ObjectMapper().getTypeFactory().constructMapType(HashMap.class, String.class, String.class));
        System.out.println(mm);
        
        //List<bean>
        CollectionType userListType = new ObjectMapper().getTypeFactory().constructCollectionType(ArrayList.class, User.class);
        List<User> userList2 = new ObjectMapper().readValue(arrayStr, userListType);
        System.out.println(userList2);//[User(name=Zhangsan, nameEn=null), User(name=null, nameEn=Lisi)]
        //List<Map>
        CollectionType mapListType = new ObjectMapper().getTypeFactory().constructCollectionType(ArrayList.class, Map.class);
        List<Map> mapList2 = new ObjectMapper().readValue(arrayStr, mapListType);
        System.out.println(mapList2);//[{name=Zhangsan}, {nameEn=Lisi}]
        
        //复杂类型
        String complexStr1 = getComplex1(); //List<Map<String, User>>
        String complexStr2 = getComplex2(); //Map<String,List<User>>
        
        //先构造内部结构innerType
        JavaType innerType = objectMapper.getTypeFactory().constructParametricType(Map.class, String.class, User.class);
        //再构造List<innerType>结构
        JavaType resultType = objectMapper.getTypeFactory().constructParametricType(List.class, innerType);
        List<Map<String,User>> list1 = objectMapper.readValue(complexStr1, resultType);
        System.out.println(list1); //[{1={name=zs, nameEn=zs_en}}, {3={name=ls, nameEn=ls_en}}]
        
        //先构造右边部分rightType
        JavaType rightType = objectMapper.getTypeFactory().constructParametricType(List.class, User.class);
        //在构造全局Map<String,rightType>
        JavaType resultType1 = objectMapper.getTypeFactory().constructParametricType(Map.class, String.class, rightType.getRawClass());
        Map<String,List<User>> map1 = objectMapper.readValue(complexStr2, resultType1);
        System.out.println(map1); //{1=[{name=tt, nameEn=tt1}], 2=[{name=ww, nameEn=ww1}]}
    }

3.3)TyepReference和JavaType区别:

TypeReference比javaType模式更加方便,代码也更加简洁,看一下例子:

//JavaType list<bean>
@Test
public void test4() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, Student.class);

    List<Student> list = new ArrayList<>();
    list.add(new Student("adai",21));
    list.add(new Student("apei",22));
    String json = mapper.writeValueAsString(list);
    List<Student> student2 = mapper.readValue(json, javaType);
    System.out.println(student2.get(0).getName());
}

//JavaType Map<String,Bean>
@Test
public void test5() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    // 第二个参数是Map的key,第三个参数是Map的value
    JavaType javaType = mapper.getTypeFactory().constructParametricType(Map.class, String.class, Student.class); 

    Map<String, Student> map = new HashMap<>();
    map.put("first",new Student("adai",21));
    map.put("second",new Student("apei",22));
    String json = mapper.writeValueAsString(map);
    Map<String, Student> result = mapper.readValue(json, javaType);
    System.out.println(result.get("first").getName());
}


//TypeReference list<bean>
@Test
public void test6() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    List<Student> list = new ArrayList<>();
    list.add(new Student("adai",21));
    list.add(new Student("apei",22));
    String json = mapper.writeValueAsString(list);
    List<Student> student2 = mapper.readValue(json, new TypeReference<List<Student>>(){}); 
    System.out.println(student2.get(0).getName());
}
//TypeReference Map<Strin,bean>
@Test
public void test7() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    Map<String, Student> map = new HashMap<>();
    map.put("first",new Student("adai",21));
    map.put("second",new Student("apei",22));
    String json = mapper.writeValueAsString(map);
    Map<String, Student> result = mapper.readValue(json, new TypeReference<Map<String,Student>>(){});
    System.out.println(result.get("first").getName());
}

4、其他

readValue出了上面从String读取json字符串外,还支持从InputStrea、URL等源读取json字符串,然后进行转换。例如:

InputStream in = null;
Map m = objectMapper.readValue(in, Map.class);

Jackson还有一个很有意思的功能,虽然没有广泛的被人所知道。那就是POJO和POJO之间的转换。概念性的可以理解成POJO1->JSON->POJO2,但是实际上会省略中间这一步,不会真正的生成JSON,而会用其他更高效的实现:

ResultType result = mapper.convertValue(sourceObject, ResultType.class);

例如:

// List<Integer> -> int[]
List<Integer> sourceList = ...;
int[] ints = mapper.convertValue(sourceList, int[].class);
// POJO -> Map
Map<String,Object> propertyMap = mapper.convertValue(pojoValue, Map.class);
// Map -> POJO
PojoType pojo = mapper.convertValue(propertyMap, PojoType.class);
// decode Base64! (default byte[] representation is base64-encoded String)

设置,还可以做base64

//解码
String base64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz";
byte[] binary = mapper.convertValue(base64, byte[].class);
System.out.println(new String(binary));
//编码
String str = "Man is distinguished, not only by his reason, but by this";
String base = mapper.convertValue(str.getBytes(), String.class);
System.out.println(base);

二、convertValue()方法

从方法名字上来看,该方法是用来做转换的,将一个object转成其他object或List<object>,不能将json字符串转成object。所以和readValue有个本质的区别是:convertValue方法输入的是对象,而不是字符串

convertValue同readValue一样,也有三个重载:

  • convertValue(Object fromValue, Class<T> toValueType) 
  • convertValue(Object fromValue, TypeReference<T> valueTypeRef) 
  • convertValue(Object fromValue, JavaType javaType) 

接下来只以convertValue(Object fromValue, TypeReference<T> valueTypeRef)  为例看一下:

private static void convertTest() {
    System.out.println("-----------------");
    //convertValue三种用法:将object转成object 或 List<object>
    //注:不能将string转成object 下面这两种都会报错
    //        User uu = new ObjectMapper().convertValue(jsonStr, User.class);
    //        System.out.println(uu);
    //        
    //        Map mm = new ObjectMapper().convertValue(jsonStr, Map.class);
    //        System.out.println(mm);
    
    //bean > map
    User u = new User("aaa","aaa_en");
    Map<String,String> map1 = new ObjectMapper().convertValue(u, new TypeReference<Map<String,String>>(){});
    System.out.println(map1); //{name=aaa, nameEn=aaa_en}
    
    //map > bean
    User user1 = new ObjectMapper().convertValue(map1, new TypeReference<User>(){});
    System.out.println(user1); //User(name=aaa, nameEn=aaa_en)
    
    //List<bean> > List<Map>
    List<User> list1 = new ArrayList<>();
    list1.add(user1);
    List<Map<String,String>> list2 = new ObjectMapper().convertValue(list1, new TypeReference<List<Map<String,String>>>(){});
    System.out.println(list2); //[{name=aaa, nameEn=aaa_en}]
    
    //list<Map> > list<Bean>
    List<User> list3 = new ObjectMapper().convertValue(list1, new TypeReference<List<User>>(){});
    System.out.println(list3); //[User(name=aaa, nameEn=aaa_en)]
}

三、bean和map之间的转换方法

根据上面可知使用jackson的convertValue方法可以在bean和map之间进行转换。此外,还可以通过如下方法:

1、Apache commons-beanutils工具:

<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>

示例:

public static void main(String... strings) throws Exception {
    User u = new User();
    u.setName("aaa");
    u.setNameEn("bbb");

    //bean to map
    Map describe = new BeanMap(u);
    System.out.println(describe); //BeanMap<JsonUtils.User(name=aaa, nameEn=bbb)>
    
    //bean to map
    Map<String, String> map = BeanUtils.describe(u);
    //{name=aaa, nameEn=bbb, class=class com.tencent.rating.common.utils.JsonUtils$User}
    System.out.println(map);
    
    //map to bean
    User uu = new User();
    BeanUtils.populate(uu, describe);
    System.out.println(uu); //JsonUtils.User(name=aaa, nameEn=bbb)
}

@Data
public static class User {
    private String name;
    private String nameEn;
}

注:使用describe方法将bean转成map时,会多一个class信息在map中。

https://www.hicode.club/articles/2018/03/18/1550590751627.html

https://juejin.cn/post/6844903694379548679

https://www.cnblogs.com/AdaiCoffee/p/10933091.html#3-jackson-%E5%A4%84%E7%90%86%E6%B3%9B%E5%9E%8B%E8%BD%AC%E6%8D%A2

  • 19
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路人儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值