Jackson的使用及注意事项

一、导入依赖

  • 如果使用的是SpringBoot则无需导入,SpringBoot近几个版本以内置JackSon
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.4</version>
</dependency>

二、在Spring中配置Jackson(如果是SpringMVC的话,可能存在乱码问题,所以要进行配置,SpringBoot可跳过此部分)

1、使用xml进行配置

<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
        <constructor-arg value="UTF-8"/>
    </bean>
    <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="objectMapper">
            <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                <property name="failOnEmptyBeans" value="false"/>
            </bean>
        </property>
    </bean>
</mvc:message-converters>
</mvc:annotation-driven>

2.使用注解配置

  • 以下代码添加在Spring配置类(即被@Configuration标注的类)中,且还要添加@EnableWebMvc注解,且继承WebMvcConfigurer
// 配置消息转换器
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(stringHttpMessageConverter());
    }

    // 配置编码
    @Bean
    public StringHttpMessageConverter stringHttpMessageConverter(){

        return new StringHttpMessageConverter(UTF_8);
    }

    // 配置jackson消息转换器,虽然不配置仅靠stringHttpMessageConverter也没有乱码,但还是配置一下
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        Jackson2ObjectMapperFactoryBean jackson2ObjectMapperFactoryBean = jackson2ObjectMapperFactoryBean();

        mappingJackson2HttpMessageConverter.setObjectMapper(jackson2ObjectMapperFactoryBean.getObject());
        return  mappingJackson2HttpMessageConverter;
    }

3.不进行配置,而在所有的RequestMapping中添加produces属性值为application/json;charset=UTF-8

@RequestMapping(value = "/json1",produces = "application/json;charset=UTF-8")

三、使用Jackson

Jackson的绝大多数操作都需要通过ObjectMapper类来进行

1.基础用法

如下

ObjectMapper mapper = new ObjectMapper();
Friend friend = new Friend("PPG007", 21);

// 写为字符串
String text = mapper.writeValueAsString(friend);
// 写为文件
mapper.writeValue(new File("friend.json"), friend);
// 写为字节流
byte[] bytes = mapper.writeValueAsBytes(friend);
System.out.println(text);
// 从字符串中读取
Friend newFriend = mapper.readValue(text, Friend.class);
// 从字节流中读取
newFriend = mapper.readValue(bytes, Friend.class);
// 从文件中读取
newFriend = mapper.readValue(new File("friend.json"), Friend.class);
System.out.println(newFriend);

2.使用Jackson生成复杂JSON

  • 实际开发中,要生成的JSON往往没有对应的实体类,一般使用Map存储键值对然后使用ObjectMapper生成JSON字符串
public void test() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    HashMap<String, Object> hashMap = new HashMap<>();
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    hashMap.put("editTime",simpleDateFormat.format(new Date()));
    hashMap.put("name","PPG007");
    hashMap.put("sex","male");
    hashMap.put("age",21);
    String value = objectMapper.writeValueAsString(hashMap);
    System.out.println(value);
}

输出:

{"sex":"male","name":"PPG007","editTime":"2021-07-07 19:27:29","age":21}

这里要注意,如果HashMap中某个键已经是被转换过的JSON字符串了,则再次进行转换会出现引号丢失、多余转义字符等问题,即生成了不标准JSON,在Layui等框架中可能会出现无法解析的问题
像下面这样

public void test() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    HashMap<String, Object> hashMap = new HashMap<>();
    HashMap<String, String> hashMap2 = new HashMap<>();
    hashMap2.put("name","teacher");
    hashMap2.put("age","40");
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    hashMap.put("editTime",simpleDateFormat.format(new Date()));
    hashMap.put("name","PPG007");
    hashMap.put("sex","male");
    hashMap.put("age",21);
    hashMap.put("teacher",objectMapper.writeValueAsString(hashMap2));
    String value = objectMapper.writeValueAsString(hashMap);
    System.out.println(value);
}

输出为

{"teacher":"{"name":"teacher","age":"40"}","sex":"male","name":"PPG007","editTime":"2021-07-07 19:39:10","age":21}

正确做法应该是在最后一次转换前,所有内容都应该是Java对象,直到最后才生成JSON字符串

public void test() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    HashMap<String, Object> hashMap = new HashMap<>();
    HashMap<String, String> hashMap2 = new HashMap<>();
    hashMap2.put("name","teacher");
    hashMap2.put("age","40");
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    hashMap.put("editTime",simpleDateFormat.format(new Date()));
    hashMap.put("name","PPG007");
    hashMap.put("sex","male");
    hashMap.put("age",21);
    hashMap.put("teacher",hashMap2);
    String value = objectMapper.writeValueAsString(hashMap);
    System.out.println(value);
}

输出为

{"teacher":{"name":"teacher","age":"40"},"sex":"male","name":"PPG007","editTime":"2021-07-07 19:41:01","age":21}

  • 如果要生成JSON列表,则使用List存储HashMap或实体类然后使用ObjectMapper生成JSON即可
public void test() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    HashMap<String, Object> hashMap = new HashMap<>();
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    LinkedList<HashMap<String, Object>> hashMaps = new LinkedList<>();
    for (int i = 0; i < 5; i++) {
        hashMap.put("editTime",simpleDateFormat.format(new Date()));
        hashMap.put("name","PPG007");
        hashMap.put("sex","male");
        hashMap.put("age",20+i);
        //这里要深拷贝,如果不使用clone方法,由于传入的是引用,下面的clear方法会清空被引用的HashMap导致list中没有值
        hashMaps.add((HashMap<String, Object>) hashMap.clone());
        hashMap.clear();
    }
    String value = objectMapper.writeValueAsString(hashMaps);
    System.out.println(value);
}

输出

[{"name":"PPG007","editTime":"2021-07-07 19:31:36","age":20,"sex":"male"},{"name":"PPG007","editTime":"2021-07-07 19:31:36","age":21,"sex":"male"},{"name":"PPG007","editTime":"2021-07-07 19:31:36","age":22,"sex":"male"},{"name":"PPG007","editTime":"2021-07-07 19:31:36","age":23,"sex":"male"},{"name":"PPG007","editTime":"2021-07-07 19:31:36","age":24,"sex":"male"}]

3.使用Jackson解析复杂JSON

如何从一个JSONList解析成对象List呢?
使用readTree方法,遍历其中的JsonNode

JsonNode jsonNode = mapper.readTree(s);
for (JsonNode node : jsonNode) {
    System.out.print(node.get("age"));
    //asText方法会去掉引号
    System.out.print(node.get("name").asText());
    System.out.println(node.get("tel").asText());
}

如果JSONList中是严格的对象List,则直接readValue为List也是可以的,但是会失去泛型
在解析不规则JSON时,JsonNode是更好的选择
有如下JSON

{
  "data": {
    "observe": {
      "degree": "24",
      "humidity": "89",
      "precipitation": "0",
      "pressure": "1003",
      "update_time": "202107071650",
      "weather": "多云",
      "weather_code": "01",
      "weather_short": "多云",
      "wind_direction": "3",
      "wind_power": "2"
    }
  },
  "message": "OK",
  "status": 200
}

我们要获取其中观察到的天气(当然应该先判断status是否成功)使用JsonNode即可

public void test() throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    String json = FileUtils.readFileToString(new File("src/main/resources/result.json"), StandardCharsets.UTF_8);
    JsonNode jsonNode = objectMapper.readTree(json);
    System.out.println(jsonNode.get("data").get("observe").get("weather").asText());
}

当然,上面的不停get可能过于繁琐,下面几种方法更加简单

public void test() throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    String json = FileUtils.readFileToString(new File("src/main/resources/result.json"), StandardCharsets.UTF_8);
    JsonNode jsonNode = objectMapper.readTree(json);
//        返回值是JsonNode
    System.out.println(jsonNode.findValue("weather").asText());
//        返回值是JsonNode
    System.out.println(jsonNode.findPath("weather").asText());
//        获取父节点
    System.out.println(jsonNode.findParent("weather"));
//        返回值是List<JsonNode>
    System.out.println(jsonNode.findValuesAsText("weather"));
}

4.JackSon配置日期格式

public void test() throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    Date date = new Date();
    objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    System.out.println(objectMapper.writeValueAsString(date));
}

5.Jackson常用注解

  1. @JsonProperty 添加在实体类成员上,在要解析的JSON字符串键值与类成员名字不一致时使用
//将JSON字符串中image_name映射到imageName
@JsonProperty(value = "image_name")
private String imageName;
  1. @JsonIgnore用于忽略JSON字符串中没有的键,添加这个注解的实体类成员将会为null

Jackson的使用及注意事项

上一篇:Sybase 数据库新增用户,赋权


下一篇:Ceph 文件存储