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);
i例如:
// 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
|