专业的编程技术博客社区

网站首页 > 博客文章 正文

Jackson-数据交换格式利器(jackson 类型转换)

baijin 2024-08-15 17:08:26 博客文章 4 ℃ 0 评论

简介

JSON 是 JavaScript Object Notation 的缩写,JSON 是一种非常流行的数据交换格式,它通常被用来在浏览器和 web 服务器之间传递数据。因为浏览器原生支持解析 JSON 为 JavaScript 对象。在服务器,JSON 需要使用 JSON API 来解析和生成,Jackson 是最流行的 JSON API 之一。

使用 Jackson 的 ObjectMapper 类是使用 Jackson 解析 JSON 最简单的方式。ObjectMapper 可以从字符串、流或者文件解析 JSON,并创建 Java 对象或者对象图来表示解析之后的 JSON。将 JSON 解析成 Java 对象也叫做从 JSON 反序列化为 Java 对象。

ObjectMapper 也可以将 Java 对象转成 JSON,这也叫做序列化 Java 对象成 JSON。

ObjectMapper 能够解析 JSON 并转换成你开发的类的对象,或者转换成内置 JSON 树模型对象。

Jackson 使用 ObjectMapper 这个名字是因为它可以将 JSON 映射成 Java 对象(反序列化),或者将 Java 对象映射成 JSON(序列化)。

ObjectMapper 位于 Jackson Databind 这个项目中,所以你需要在你的项目中引入该项目。

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.16.0</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
<version>2.16.0</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>

Jackson库的一些主要API和组件

ObjectMapper:这是Jackson库的核心类,用于序列化和反序列化操作。主要方法有:

  • writeValueAsString(Object):将Java对象序列化为JSON字符串。
  • readValue(String, Class):将JSON字符串反序列化为Java对象。

JsonParser:用于从JSON数据源(如文件、输入流或字符串)解析JSON数据。主要方法有:

  • nextToken():获取下一个JSON令牌(如START_OBJECT、FIELD_NAME等)。
  • getValueAsString():将当前令牌作为字符串返回。
  • getValueAsInt():将当前令牌作为整数返回。

JsonGenerator:用于将JSON数据写入数据源(如文件、输出流或字符串缓冲区)。主要方法有:

  • writeStartObject():写入开始对象标记({)。
  • writeFieldName(String):写入字段名称。
  • writeString(String):写入字符串值。
  • writeEndObject():写入结束对象标记(})。

JsonNode:用于表示JSON树模型中的节点,可以是对象、数组、字符串、数字等。主要方法有:

  • get(String):获取指定字段的子节点。
  • path(String):获取指定字段的子节点,如果不存在则返回一个“missing”节点。
  • isObject():检查当前节点是否是一个对象。
  • isArray():检查当前节点是否是一个数组。

注解:Jackson提供了一系列注解来配置序列化和反序列化过程。一些常用注解包括:

  • @JsonProperty:指定字段在JSON数据中的名称。
  • @JsonIgnore:指定字段在序列化和反序列化过程中被忽略。
  • @JsonCreator:指定用于反序列化的构造函数或工厂方法。
  • @JsonSerialize:指定用于序列化特定字段或类的自定义序列化器。
  • @JsonDeserialize:指定用于反序列化特定字段或类的自定义反序列化器。

这只是Jackson库API和组件的一个概览。如果你想深入了解具体的API和使用方法,请参考官方文档(github.com/FasterXML/j… )和相关教程。同时,实际编程过程中,根据具体需求学习和了解相应的API也是非常有效的方法。

JSON 字符串转 Java 对象

将一个 JSON 字符串转成 Java 对象非常的简单,你已经看过这种例子的,就是使用的 ObjectMapper 的 readValue() 方法,下面是另一个简单例子:

ObjectMapper objectMapper = new ObjectMapper();
String carJson =
    "{ "windows" : "windows", "doors" : 4 }";

Car car = objectMapper.readValue(carJson, Car.class);

JSON Reader 转 Java 对象

你也可以从 JSON Reader 实例中读取 Java 对象,示例如下:

ObjectMapper objectMapper = new ObjectMapper();

File file = new File("data/car.json");

Car car = objectMapper.readValue(file, Car.class);

JSON 文件转 Java 对象

从文件中读取 JSON 可以考虑通过 FileReader 类来完成,也可以使用 File 类。示例如下:

ObjectMapper objectMapper = new ObjectMapper();

File file = new File("data/car.json");

Car car = objectMapper.readValue(file, Car.class);

JSON URL 转 Java 对象

你可能会需要从一个 URL 读取 JSON,像这样:

ObjectMapper objectMapper = new ObjectMapper();

URL url = new URL("file:data/car.json");

Car car = objectMapper.readValue(url, Car.class);

当然你也可以使用 HTTP URL,如 http://8********/car-data.json。

JSON InputStream 转 Java 对象

使用 ObjectMapper 从 InputStream 读取 JSON 也是可能的,下面的例子中展示了如何从一个 InputStream 中读取 JSON:

ObjectMapper objectMapper = new ObjectMapper();

InputStream input = new FileInputStream("data/car.json");

Car car = objectMapper.readValue(input, Car.class);

JSON Byte Array 转 Java 对象

Jackson 也支持从字节数组中读取 JSON,示例如下:

ObjectMapper objectMapper = new ObjectMapper();

String carJson =
        "{\ "windows\" : \"windows\", \"doors\" : 54}";

byte[] bytes = carJson.getBytes("UTF-8");

Car car = objectMapper.readValue(bytes, Car.class);

JSON 数组字符串转对象数组

Jackson 的 ObjectMapper 也支持从一个 JSON 数组字符串中读取对象数组,示例如下:

String jsonArray = "[{"brand":"ford"}, {"brand":"Fiat"}]";

ObjectMapper objectMapper = new ObjectMapper();

Car[] cars2 = objectMapper.readValue(jsonArray, Car[].class);

注意,Car 数组类被传递给 readValue() 方法的第二个参数是为了告诉 ObjectMapper 你想要读取一个 Car 实例的数组。读取数组对象也可以通过其他的 JSON 源,而不只是 JSON 字符串,比如,文件、URL、InputStream、Reader 等。

JSON 数组字符串转 List 对象

Jackson ObjectMapper 也可以从 JSON 数组字符串中读取到 Java List 对象,如下所示:

String jsonArray = "[{"windows":"1"}, {"windows":"2"}]";
ObjectMapper objectMapper = new ObjectMapper();
List<Car> cars1 = objectMapper.readValue(jsonArray, new TypeReference<List<Car>>(){});

注意 TypeReference 参数被传递给了 readValue() 方法,这个参数告诉 Jackson 要读取一个存储 Car 对象的 List。

JSON 字符串转 Map

Jackson ObjectMapper 也可以从 JSON 字符串中读取 Map,这在你事先不知道要解析的 JSON 结构的时候非常有用。在 JSON 对象中的每个字段对应于 Map 中的一个键值对。示例如下:

String jsonObject = "{"windows":"ffff", "doors":4}";
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(jsonObject,
    new TypeReference<Map<String,Object>>(){});


忽略不知道的 JSON 字段

某些情况下,JSON 的字段数量多于你要转成的 Java 对象中的字段数量,当发生这种情况下时,Jackson 默认会抛出异常。但是有时允许 JSON 中的字段要多于关联的 Java 对象中的字段,在这种情况下,Jackson 允许你通过 Jackson 配置来忽略额外的字段,配置如下:

objectMapper.configure(
    DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

JSON 中 Null 值对应 Java 基本数据类型

如果 JSON 字符串有一个值被设置为 null,而这个字段在关联的 Java 对象中的基本数据类型,而基本数据类型是不能设置成 null 的,因此在转换的时候 Jackson 默认会忽略掉该基本数据类型:

objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);

自定义 Deserializer

某些情况下,你可能想要通过不同于 Jackson ObjectMapper 默认行为的方式来解析 JSON 字符串为 Java 对象,这时你可以向 ObjectMapper 添加一个 custom Deserializer 来执行你想要的反序列化行为。

下面例子给 Jackson ObjectMapper 注册了一个自定义的 Deserializer:

String json = "{ "windows" : "windows", "doors" : 4 }";
SimpleModule module =
        new SimpleModule("CarDeserializer", new Version(3, 1, 8, null, null, null));
module.addDeserializer(Car.class, new CarDeserializer(Car.class));
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);

Car car = mapper.readValue(json, Car.class);

CarDeserializer.java

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class CarDeserializer extends StdDeserializer<Car> {

    public CarDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Car deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException {
        Car car = new Car();
        while(!parser.isClosed()){
            JsonToken jsonToken = parser.nextToken();

            if(JsonToken.FIELD_NAME.equals(jsonToken)){
                String fieldName = parser.getCurrentName();
                System.out.println(fieldName);

                jsonToken = parser.nextToken();

                if("brand".equals(fieldName)){
                    car.setBrand(parser.getValueAsString());
                } else if ("doors".equals(fieldName)){
                    car.setDoors(parser.getValueAsInt());
                }
            }
        }
        return car;
    }
}

Java 对象转 JSON

Jackson ObjectMapper 也可以用来从一个对象中生成 JSON,你可以以下方法之一实现这一功能:

  • writeValue()
  • writeValueAsString()
  • writeValueAsBytes()

下面的例子从 Car 对象中生成 JSON:

ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car();
car.brand = "audi";
car.doors = 4;
objectMapper.writeValue(
    new FileOutputStream("data/output-2.json"), car);

该例子首先创建了 ObjectMapper 和 Car 实例,最后调用 ObjectMapper 的 writeValue() 方法来讲 Car 实例转换成 JSON,并写入到给定的 FileOutputStream 中。

ObjectMapper 的 writeValueAsString() 方法和 writeValueAsBytes() 方法都可以从对象中生成 JSON,并以字符串和字节数组的格式返回,writeValueAsString() 方法的使用示例如下:

ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car();
car.brand = "audi";
car.doors = 4;
String json = objectMapper.writeValueAsString(car);
System.out.println(json);

自定义 Serializer

某些时候你可能需要不同于 Jackson 的默认行为来序列化一个 Java 对象到 JSON,比如,你可能想要在 JSON 中使用不同于 Java 对象的字段名,或者你想要完全忽略某些字段。

你可以给 ObjectMapper 提供自定义的 Serializer 来实现该功能。Serializer 作为一个确定类被注册,它将会在 ObjectMapper 序列化 Car 对象的时候被调用,下面例子展示了如何给 Car 类注册一个自定义 Serializer:

CarSerializer carSerializer = new CarSerializer(Car.class);
ObjectMapper objectMapper = new ObjectMapper();

SimpleModule module =
        new SimpleModule("CarSerializer", new Version(2, 1, 3, null, null, null));
module.addSerializer(Car.class, carSerializer);
objectMapper.registerModule(module);
Car car = new Car();
car.setBrand("benzs");
car.setDoors(5);

String carJson = objectMapper.writeValueAsString(car);

被 Jackson 自定义 serializer 产生的字符串看起来像下面这样:

{"producer":"Mercedes","doorCount":5}

CarSerializer.java

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
public class CarSerializer extends StdSerializer<Car> {
    ptected CarSerializer(Class<Car> t) {
        super(t);
    }

    public void serialize(Car car, JsonGenerator jsonGenerator,
                          SerializerProvider serializerProvider)
            throws IOException {

        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("producer", car.getBrand());
        jsonGenerator.writeNumberField("doorCount", car.getDoors());
        jsonGenerator.writeEndObject();
    }
}

注意,传递给 serialize() 方法的第二个参数是 Jackson JsonGenerator 实例,你可以使用它来序列化 Java 对象,在上面的例子中是 Car 对象。

日期格式

Jackson 默认将 java.util.Date 对象序列化为它的 long 类型值,它代表从 1970.1.1 至今的毫秒数。然而,Jackson 也支持序列化日期为字符串,在这一节我们将仔细研究 Jackson 日期格式。

Date to long

首先我将会向你展示 Jackson 默认的日期格式,即将 Date 序列化为 long 类型值,示例如下:

要使用 Jackson ObjectMapper 序列化一个 Transaction 对象就像你序列化其他 Java 对象一样,它的代码看起来长这样:

Transaction transaction = new Transaction("transfer", new Date());
ObjectMapper objectMapper = new ObjectMapper();
String output = objectMapper.writeValueAsString(transaction);
System.out.println(output);


打印输出如下:

{"type":"transfer","date":1516442298301}

注意看 date 字段的格式,它是一个 long 类型值。

Date to String

long 类型值对人类来说可读性并不高,因此 Jackson 也支持文本类型的日期格式。你可以通过给 ObjectMapper 设置一个 SimpleDateFormat 来给 Jackson 指定额外的日期格式,示例如下:

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
objectMapper2.setDateFormat(dateFormat);
?
String output2 = objectMapper2.writeValueAsString(transaction);
System.out.println(output2);

打印输出如下:

{"type":"transfer","date":"2018-01-20"}

JSON Tree Model(树模型)

Jackson 有一个内建的 Tree Model 用来表示一个 JSON 对象。当你不知道一个 JSON 的层级构建的时候,Jackson 的 Tree Model 非常有用。如果你需要在使用和传递 JSON 对象之前修改它,那么 Jackson Tree Model 也是非常有用的,这些场景在 Data Streaming 中非常常见。

Jackson Tree Model 用 JsonNode 类来表示,你可以使用 ObjectMapper 来解析 JSON 为一个 JsonNode 树模型,就像对你自己的类所做的那样。

下面的章节将会展示 Jackson ObjectMapper 如何读取和写入 JsonNode 实例。

下面是一个 Jackson Tree Model 的简单示例:

String carJson =
        "{ "brand" : "Mercedes", "doors" : 4 }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);
} catch (IOException e) {
    e.printStackTrace();
}

正如你所看到的,JSON 字符串被解析成了一个 JsonNode 对象而不是 Car 对象,只需要传递 JsonNode.class 给 readValue() 方法的第二个参数即可。

ObjectMapper 也有一个特殊方法 readTree(),该方法总是返回一个 JsonNode,如下所示:

String carJson =
        "{ "brand" : "Mercedes", "doors" : 5 }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readTree(carJson);
} catch (IOException e) {
    e.printStackTrace();
}

JsonNode 使你能够以一种非常灵活和动态的方法将 JSON 作为 Java 对象来导航。下面会有专门讲解 JsonNode 的章节,这里会展示它的基础用法。

一旦你将 JSON 解析成 JsonNode 之后,你将能够导航该 JsonNode Tree Model,下面是一个简单的例子展示了如何访问 JSON 字段、数组和内嵌对象。

String carJson =
        "{ "brand" : "Mercedes", "doors" : 4," +
        "  "owners" : ["John", "Jack", "Jill"]," +
        "  "nestedObject" : { "field" : "value" } }";
ObjectMapper objectMapper = new ObjectMapper();
try {
    JsonNode jsonNode = objectMapper.readValue(carJson, JsonNode.class);
    JsonNode brandNode = jsonNode.get("brand");
    String brand = brandNode.asText();
    System.out.println("brand = " + brand);
    JsonNode doorsNode = jsonNode.get("doors");
    int doors = doorsNode.asInt();
    System.out.println("doors = " + doors);
    JsonNode array = jsonNode.get("owners");
    JsonNode jsonNode = array.get(0);
    String john = jsonNode.asText();
    System.out.println("john  = " + john);
    JsonNode child = jsonNode.get("nestedObject");
    JsonNode childField = child.get("field");
    String field = childField.asText();
    System.out.println("field = " + field);
} catch (IOException e) {
    e.printStackTrace();
}

注意现在的 JSON 字符串包含一个名为 owners 的数组字段和一个内嵌的对象字段叫 nestedObject。

不管你是在访问字段、数组还是内嵌对象,你都可以使用 JsonNode 的 get() 方法,给 get() 方法提供字符串作为参数你可以访问 JsonNode 的字段,如果这个 JsonNode 表示一个数组,你需要传递 index 到 get() 方法中,index 指定了元素在数组中的位置。

Java 对象转 JsonNode

使用 Jackson ObjectMapper 将 Java 对象转换成 JsonNode 是可能的,此时 JsonNode 是被转换的 Java 对象的 JSON 表现形式。你可以通过 ObjectMapper 的 valueToTree() 方法来将 Java 对象转换成 JsonNode,示例如下:

ObjectMapper objectMapper = new ObjectMapper();
?
Car car = new Car();
car.brand = "audi";
car.doors = 4;
?
JsonNode carJsonNode = objectMapper.valueToTree(car);

JsonNode 转 Java 对象

你可以通过使用 ObjectMapper 的 treeToValue() 方法来将 JsonNode 转换成 Java 对象,这类似于使用 ObjectMapper 将 JSON 字符串转成 Java 对象。唯一不同的是,JSON 源是 JsonNode。示例如下:

ObjectMapper objectMapper = new ObjectMapper();

String carJson = "{ "brand" : "BWM", "doors" : 5 }";
JsonNode carJsonNode = objectMapper.readTree(carJson);
Car car = objectMapper.treeToValue(carJsonNode)

上面的例子有点 “人造的”,因为我先将 JSON 字符串转成 JsonNode 然后再将 JsonNode 转成 Car 对象。显然,你也可以直接将 JSON 字符串转成 Car 对象,而不需要先转成 JsonNode。

读取 JSON 字符串到 JsonNode

读取 JSON 字符串到 JsonNode 中,你需要先创建 ObjectMapper 实例,然后调用 readTree() 方法并传入 JSON 源作为参数,下面是一个反序列化 JSON 字符串到 JsonNode 的简单示例:

String json = "{ "f1" : "v1" } ";
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(json);
System.out.println(jsonNode.get("f1").asText());

遍历 JsonNode 图

表示 JSON 对象或 JSON 数组的 JsonNode 可以像其他对象图一样被迭代,你可以迭代它的内嵌字段来做到这一点,示例如下:

public static void traverse(JsonNode root){
    if(root.isObject()){
        Iterator<String> fieldNames = root.fieldNames();
        while(fieldNames.hasNext()) {
            String fieldName = fieldNames.next();
            JsonNode fieldValue = root.get(fieldName);
            traverse(fieldValue);
        }
    } else if(root.isArray()){
        ArrayNode arrayNode = (ArrayNode) root;
        for(int i = 0; i < arrayNode.size(); i++) {
            JsonNode arrayElement = arrayNode.get(i);
            traverse(arrayElement);
        }
    } else {
        // JsonNode root represents a single value field - do something with it.
    }
}

需要修改这个例子来填充你自己的用例,准确的实现依赖于你需要通过迭代节点做些什么,然而,你可以使用上面的例子作为模板,然后修改它直到符合你的需要。

ObjectNode

在之前我们提到过 JsonNode 是不可变的,但是要创建 JsonNode 对象图,你需要改变 JsonNode 对象图,比如设置属性值和孩子节点等,由于 JsonNode 的不可变性,我们不能直接创建一个 JsonNode。

相反,你可以创建一个 JsonNode 的子类 ObjectNode,通过 ObjectMapper 的 createObjectNode() 方法创建一个 ObjectNode,示例如下:

ObjectMapper objectMapper = new ObjectMapper();
ObjectNode objectNode = objectMapper.createObjectNode();

删除字段

ObjectNode 类有一个 remove() 方法,它用来从 ObjectNode 中删除字段,示例如下:

objectNode.remove("fieldName");

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表