写点什么

轻松上手 Jackjson(珍藏版)

  • 2024-04-08
    福建
  • 本文字数:21532 字

    阅读完需:约 71 分钟

写在前面


虽然现在市面上有很多优秀的 json 解析库,但 Spring 默认采用 Jackson 解析 Json。

本文将通过一系列通俗易懂的代码示例,带你逐步掌握 Jackson 的基础用法、进阶技巧以及在实际项目中的应用场景。


一、Jackjson 简介


Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。

什么是序列化和反序列化呢?

  • 序列化:将 Java Bean 转换为JSON 字符串

  • 反序列化:将JSON字符串转换为JavaBeen对象

GitHub 地址:https://github.com/FasterXML/jackson

从 GitHub 看到,目前有 8.8k stars,最近更新时间是 2 个月前。可见其更新速度还是比较活跃的

也是 json 最流行的解析器之一


二、Jackjson 优点


  • Jackson 所依赖的 jar 包较少,简单易用

  • 其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比较快

  • Jackson 运行时占用内存比较低,性能比较好

  • Jackson 有灵活的 API,可以很容易进行扩展和定制


三、Jackjson 模块及依赖


① Jackson 库包括三个主要的模块

  • jackson-databind 用于数据绑定

  • jackson-core 用于 JSON 解析和生成

  • jackson-annotations 用于注解支持


② 所需依赖

jackson-databind 依赖 jackson-core 和 jackson-annotations,所以可以只显示地添加 jackson-databind 依赖,jackson-core 和 jackson-annotations 也随之添加到 Java 项目工程中


maven

<dependency>    <groupId>com.fasterxml.jackson.core</groupId>    <artifactId>jackson-core</artifactId>    <version>2.15.3</version></dependency><dependency>    <groupId>com.fasterxml.jackson.core</groupId>    <artifactId>jackson-annotations</artifactId>    <version>2.15.2</version></dependency><dependency>    <groupId>com.fasterxml.jackson.core</groupId>    <artifactId>jackson-databind</artifactId>    <version>2.15.2</version></dependency>
复制代码


Gradle

dependencies {    implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4'}
复制代码


四、快速上手


假设我们有一个简单的 User 类:

@Data@ToString@AllArgsConstructor@NoArgsConstructorpublic class User {    private String name;    private int age;}
复制代码


4.1 Java 对象转 JSON 字符串

@Testpublic void testSerializationExample() throws JsonProcessingException {    User user = new User("小凡", 18);    ObjectMapper objectMapper = new ObjectMapper();    String userstr = objectMapper.writeValueAsString(user);    System.out.println(userstr);}//输出{"name":"小凡","age":18}
复制代码


4.2 JSON 字符串转 Java 对象

@Testpublic void testDeserializationExample() throws JsonProcessingException {    String json ="{\"name\":\"小凡\",\"age\":18}";    ObjectMapper objectMapper = new ObjectMapper();    User user = objectMapper.readValue(json, User.class);    System.out.println(user); }//输出User(name=小凡, age=18)
复制代码


上面例子中我们我们使用了ObjectMapper 它是 Jackson 库的核心类,负责实现 Java 对象与 JSON 文本之间的相互转换


五、Jackjson 序列化 API


5.1 普通 Java 对象序列化

@Testpublic void testObjectToJson() throws JsonProcessingException {    User user = new User();    user.setName("小凡");    user.setAge(18);    ObjectMapper mapper = new ObjectMapper();    String userstr = mapper.writeValueAsString(user);    System.out.println(userstr);}//输出{"name":"小凡","age":18}
复制代码


5.2 复杂对象序列化


① 构造作者出版信息对象

//书籍对象@Datapublic class Book {    private String bookName;    private String publishDate;    private String publishHouse;    private Double price; }//地址对象@Datapublic class Address {    private String city;    private String street;}//作者出版信息对象@Datapublic class PublishInfo {    private String name;    private String sex;    private Integer age;    private Address addr;    private List<Book> books;}
复制代码


② 复杂对象转 JSON 字符串

@Testpublic void testComplexObjectToJson() throws JsonProcessingException {     //构造所有出版的书籍list    ArrayList<Book> books = new ArrayList<Book>();    Book book1 = new Book();    book1.setBookName("Java从入门到放弃");    book1.setPublishDate("2004-01-01");    book1.setPublishHouse("小凡出版社");    book1.setPrice(66.66);     Book book2 = new Book();    book2.setBookName("Spring从入门到入土");    book2.setPublishDate("2024-01-01");    book2.setPublishHouse("小凡出版社");    book2.setPrice(88.88);     books.add(book1);    books.add(book2);     //构造作者地址信息    Address addr = new Address();    addr.setCity("昆明");    addr.setStreet("xxx区xxx路xxx号");     //构造作者出版的所有书籍信息    PublishInfo publishInfo = new PublishInfo();    publishInfo.setName("小凡");    publishInfo.setSex("男");    publishInfo.setAge(18);    publishInfo.setAddr(addr);    publishInfo.setBooks(books);     ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(publishInfo);    System.out.println(json);}//返回{"name":"小凡","sex":"男","age":18,"addr":{"city":"昆明","street":"xxx区xxx路xxx号"},"books":[{"bookName":"Java从入门到放弃","publishDate":"2004-01-01","publishHouse":"小凡出版社","price":66.66},{"bookName":"Spring从入门到入土","publishDate":"2024-01-01","publishHouse":"小凡出版社","price":88.88}]}
复制代码


5.3 List 集合序列化

 @Testpublic void testListToJson() throws JsonProcessingException {    User user1 = new User();    user1.setName("小凡001");    user1.setAge(18);     User user2 = new User();    user2.setName("小凡002");    user2.setAge(30);     ArrayList<User> users = new ArrayList<>();    users.add(user1);    users.add(user2);     ObjectMapper mapper = new ObjectMapper();    String userstr = mapper.writeValueAsString(users);    System.out.println(userstr); }//输出[{"name":"小凡001","age":18},{"name":"小凡002","age":30}]
复制代码


5.4 Map 集合序列化


@Testpublic void testMapToJson() throws JsonProcessingException {    User user = new User();    user.setName("小凡");    user.setAge(18);     List<String> asList = Arrays.asList("抽烟", "喝酒", "烫头发");    HashMap<String, Object> map = new HashMap<>();    map.put("user", user);    map.put("hobby",asList);     ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(map);    System.out.println(json);}//输出{"user":{"name":"小凡","age":18},"hobby":["抽烟","喝酒","烫头发"]}
复制代码


@Testpublic void testMapToJsonSup() throws JsonProcessingException {    User user1 = new User();    user1.setName("小凡001");    user1.setAge(18);     User user2 = new User();    user2.setName("小凡002");    user2.setAge(30);     ArrayList<User> users = new ArrayList<>();    users.add(user1);    users.add(user2);      List<String> asList = Arrays.asList("抽烟", "喝酒", "烫头发");    HashMap<String, Object> map = new HashMap<>();    map.put("users", users);    map.put("hobby",asList);    map.put("name","张三");     ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(map);    System.out.println(json);}//输出{"name":"张三","users":[{"name":"小凡001","age":18},{"name":"小凡002","age":30}],"hobby":["抽烟","喝酒","烫头发"]}
复制代码


5.5 日期处理


默认情况下,jackjson 会将日期类型属性序列化成 long 型值(自 1970 年 1 月 1 日以来的毫秒数)。显然这样格式的数据不符合人类直观查看


假设我们有个Person对象

@Datapublic class Person {    private String name;    private Date birthday;}
复制代码


① 我们先来看看默认转换的结果

@Testpublic void testDateToJsonDefault() throws JsonProcessingException {    Person person = new Person();    person.setName("小凡");    person.setBirthday(new Date());     ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(person);    System.out.println(json); }//输出{"name":"小凡","birthday":1712220896407}
复制代码


② 通过SimpleDateFormat 将日期格式化成人类可看格式显示

@Testpublic void testDateToJson() throws JsonProcessingException {    Person person = new Person();    person.setName("小凡");    person.setBirthday(new Date());     ObjectMapper mapper = new ObjectMapper();    //进行一下日期转换    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");    mapper.setDateFormat(dateFormat);    String json = mapper.writeValueAsString(person);    System.out.println(json); }//输出  这个格式就人性化多了{"name":"小凡","birthday":"2024-04-04"}
复制代码


六、Jackjson 反序列化 API


Jackson 通过将 JSON 字段的名称与 Java 对象中的 getter 和 setter 方法进行匹配,将 JSON 对象的字段映射到 Java 对象中的属性。

Jackson 删除了 getter 和 setter 方法名称的“ get”和“ set”部分,并将其余名称的第一个字符转换为小写。


6.1 普通 JSON 字符串反序列化


6.1.1 JSON 字符串->Java 对象


注: 这里我们还是使用前面小节中创建的User 实体类

@Testpublic void testStrToObject() throws JsonProcessingException {    String json ="{\"name\":\"小凡\",\"age\":18}";    ObjectMapper mapper = new ObjectMapper();    User user = mapper.readValue(json, User.class);    System.out.println(user);}//输出User(name=小凡, age=18)
复制代码


6.1.2 字符输入流-->Java 对象
@Testpublic void testReaderToObject() throws JsonProcessingException {    String json ="{\"name\":\"小医仙\",\"age\":18}";    Reader reader = new StringReader(json);    ObjectMapper mapper = new ObjectMapper();    User user = mapper.readValue(json, User.class);    System.out.println(user);}//输出User(name=小医仙, age=18)
复制代码


6.1.3 字节输入流->Java 对象


① 创建 user001.json 文件,文件内容如下



②将字节输入流转换为User对象

@Testpublic void testInputStreamToObject() throws IOException {    FileInputStream inputStream = new FileInputStream("F:\\vueworkspace\\jackjson-demo\\jackjson-demo\\src\\json\\user001.json");    ObjectMapper mapper = new ObjectMapper();    User user = mapper.readValue(inputStream, User.class);    System.out.println(user);	}}//输出User(name=萧炎, age=18)
复制代码


6.1.4 JSON 文件反->Java 对象


①我们准备一个 user.json 文件,内容如下



② 读取user.json文件中内容,并转换成User 对象

@Testpublic void testJsonfileToObject() throws IOException {    File file = new File("F:\\vueworkspace\\jackjson-demo\\jackjson-demo\\src\\json\\user.json");    ObjectMapper mapper = new ObjectMapper();    User user = mapper.readValue(file, User.class);    System.out.println(user); }//输出User(name=萧炎, age=18)
复制代码


6.1.5 URL 文件->Java 对象


① 我们在网络上放一个user.json资源文件,内容如下

{"name":"紫妍","age":18}
复制代码


② 通过 URL(java.net.URL) 将 JSON 转换成User对象

@Testpublic void testUrlToObject() throws IOException {    String url ="https://files.cnblogs.com/files/blogs/685650/user.json";    URL url1 = new URL(url);    ObjectMapper mapper = new ObjectMapper();    User user = mapper.readValue(url1, User.class);    System.out.println(user);}//输出User(name=紫妍, age=18)
复制代码


6.1.6 字节数组-> java 对象
@Testpublic void testByteToObject() throws IOException {    String json ="{\"name\":\"韩雪\",\"age\":18}";    byte[] bytes = json.getBytes();    ObjectMapper mapper = new ObjectMapper();    User user = mapper.readValue(bytes, User.class);    System.out.println(user); }//输出User(name=韩雪, age=18)
复制代码


6.2 JSON 数组字符串 反序列化

6.2.1 JSON 字符串->Map 集合
@Testpublic void testJsonStrToMap() throws JsonProcessingException {    //{"name":"小凡","sex":"男","age":18}    String json ="{\"name\":\"小凡\",\"sex\":\"男\",\"age\":18}";    ObjectMapper mapper = new ObjectMapper();    Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>() {});    System.out.println(map);}//输出{name=小凡, sex=男, age=18}
复制代码


6.2.2 JSON 数组字符串->List 集合
@Testpublic void testJsonArrToList() throws JsonProcessingException {    //[{"name":"小凡001","age":18},{"name":"小凡002","age":30}]    String json ="[{\"name\":\"小凡001\",\"age\":18},{\"name\":\"小凡002\",\"age\":30}]";    ObjectMapper mapper = new ObjectMapper();    List<User> users = mapper.readValue(json, new TypeReference<List<User>>() {    });    System.out.println(users);}//输出[User(name=小凡001, age=18), User(name=小凡002, age=30)]
复制代码


6.2.3 JSON 数组字符串->Java 对象数据
@Testpublic void testJsonArrToObjArr() throws JsonProcessingException {    //[{"name":"小凡001","age":18},{"name":"小凡002","age":30}]    String json ="[{\"name\":\"小凡001\",\"age\":18},{\"name\":\"小凡002\",\"age\":30}]";    ObjectMapper mapper = new ObjectMapper();    User[] users = mapper.readValue(json, User[].class);    for (User user : users) {        System.out.println(user);    }}//输出User(name=小凡001, age=18)User(name=小凡002, age=30)
复制代码


七、自定义序列化反序列化


通过自定义序列化和反序列化可以使其更加灵活多变


7.1 自定义序列化


有时候,我们不需要 jackjson 默认的序列化方式。例如,JSON 中使用与 Java 对象中不同的属性名称,

或者不需要 Java 对象中的某个字段,这时我们就需要自定义序列化器


我们先来看看 User 实体对象的定义如下

@Datapublic class User {    private String name;    private Integer age;}
复制代码


按照下面默认序列化代码,我们最后得到的 JSON 字符串如下

@Testpublic void testSerializationExample() throws JsonProcessingException {    User user = new User("小凡", 18);    ObjectMapper objectMapper = new ObjectMapper();    String userstr = objectMapper.writeValueAsString(user);    System.out.println(userstr);}//输出{"name":"小凡","age":18}
复制代码


而现在的需求升级了,再不创建或修改Users 实体对象的情况下,我们想要得到{"username":"小凡","userage":18} 这样的字符串,

应该怎么办呢?

这时,我们就需要自定义序列化器,就可以轻松实现,具体代码如下

 @Testpublic void testDefineSerialize() throws JsonProcessingException {     SimpleModule version1Module = new SimpleModule();    version1Module.addSerializer(User.class, new JsonSerializer<User>() {        @Override        public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {            jsonGenerator.writeStartObject();            jsonGenerator.writeStringField("username", user.getName());            jsonGenerator.writeNumberField("userage", user.getAge());            jsonGenerator.writeEndObject();        }    });     ObjectMapper objectMapper = new ObjectMapper();     objectMapper.registerModule(version1Module);     User user = new User("小凡", 18);     String json = objectMapper.writeValueAsString(user); // 使用版本1的序列化器     System.out.println( json); }//输出{"username":"小凡","userage":18}
复制代码


7.2 自定义反序列化器


同理,反序列化也可以自定义,具体代码如下


① 自定义一个反序列化器

public class UserDeserializer extends StdDeserializer<User> {     public UserDeserializer() {        this(null);    }     public UserDeserializer(Class<?> vc) {        super(vc);    }     @Override    public User deserialize(JsonParser jp, DeserializationContext ctxt)        throws IOException {        JsonNode node = jp.getCodec().readTree(jp);        String name = node.get("name").asText();        int age = (Integer) ((IntNode) node.get("age")).numberValue();         return new User(name, age);    }}
复制代码


②将 JSON 字符串反序列化为 User 对象

@Testpublic void testUserDeserializer() throws JsonProcessingException {    ObjectMapper objectMapper = new ObjectMapper();    SimpleModule module = new SimpleModule();    module.addDeserializer(User.class, new UserDeserializer());    objectMapper.registerModule(module);     String json = "{\"name\":\"小凡\",\"age\":30}";    User user = objectMapper.readValue(json, User.class);     System.out.println(user); }//输出User(name=小凡, age=30)
复制代码


八、树模型


Jackson 具有内置的树模型,可用于表示 JSON 对象。Jackson 树模型由 JsonNode 类表示。

在处理 JSON 时,我们有时并不直接关心或无法直接映射到特定的 Java 对象,而是需要对 JSON 内容进行动态、灵活的操作。

这时,树模型就派上了用场。我们可以遍历、查询、更新或合并 JSON 数据,而无需预先定义对应的 Java 类


8.1 JsonNode 类型概览


JsonNode 家族包括以下主要子类型:

  • ObjectNode:对应 JSON 对象,包含一组键值对,可以通过.put(key, value)添加或更新属性。

  • ArrayNode:对应 JSON 数组,包含一系列元素,可通过.add(value)插入新元素。

  • TextNodeIntNodeLongNodeDoubleNode 等:分别对应 JSON 中的字符串、整数、长整数、浮点数等基本类型值。

  • BooleanNodeNullNode:分别对应 JSON 中的布尔值和 null 值。


8.2 创建与操作 JsonNode 实例


下面通过一些具体的代码示例,展示如何使用 JackJson 的树模型 API 来创建、操作 JsonNode 对象。

示例 1:创建简单 JsonNode

@Testpublic void testJackJsonTreeModelExample(){    // 创建 ObjectMapper 实例    ObjectMapper mapper = new ObjectMapper();     // 创建并初始化各类型 JsonNode 实例    ObjectNode personNode = mapper.createObjectNode();    personNode.put("name", "小凡");    personNode.put("age", 18);     ArrayNode hobbiesNode = mapper.createArrayNode();    hobbiesNode.add("写代码").add("看书").add("打豆豆");     personNode.set("hobbies", hobbiesNode);    // 输出构建的 JSON 字符串    System.out.println(personNode.toString());}//输出{"name":"小凡","age":18,"hobbies":["写代码","看书","打豆豆"]}
复制代码


上述代码首先创建了一个ObjectMapper实例,它是 JackJson 的核心工具类,负责序列化和反序列化工作。接着,我们创建了一个ObjectNode代表 JSON 对象,设置了"name"和"age"两个属性。然后创建了一个ArrayNode存储爱好列表,并将其添加到personNode中。最后,打印输出构建的 JSON 字符串。


示例 2:从 JSON 字符串反序列化为 JsonNode

@Testpublic void testJackJsonTreeModelExample2() throws JsonProcessingException {    String jsonInput = "{\"name\":\"小凡\",\"age\":18,\"hobbies\":[\"写代码\",\"看书\",\"打豆豆\"]}";     ObjectMapper mapper = new ObjectMapper();    JsonNode rootNode = mapper.readTree(jsonInput);    System.out.println(rootNode.get("name").asText());    System.out.println(rootNode.get("age").asInt());    System.out.println(rootNode.get("hobbies").get(0).asText());    System.out.println(rootNode.get("hobbies").get(1).asText());    System.out.println(rootNode.get("hobbies").get(2).asText());}//输出小凡18写代码看书打豆豆
复制代码


在此示例中,我们首先定义了一个 JSON 字符串。然后使用ObjectMapperreadTree()方法将其反序列化为 JsonNode。接下来,通过调用.get(key)方法访问对象属性或数组元素,并使用.asText().asInt()等方法获取其值。


8.3 利用 JackJson 树模型进行深度查询、修改、合并等操作


上面给出的两个案例属于入门级操作,我们学会后可以接着进行一些高级操作

@Testpublic   void testJsonManipulationExample() throws JsonProcessingException {    String JSON_STRING = "{\"name\":\"小凡\",\"age\":18,\"hobbies\":[\"写代码\",\"看书\",\"打豆豆\"]}";    ObjectMapper mapper = new ObjectMapper();     // 将JSON字符串转换为JsonNode对象    JsonNode rootNode = mapper.readTree(JSON_STRING);     // **深度查询**    // 查询"年龄"    int age = rootNode.get("age").asInt();    System.out.println("Age: " + age);     // 查询第一个兴趣爱好    String firstHobby = rootNode.get("hobbies").get(0).asText();    System.out.println("First hobby: " + firstHobby);     // **修改**    // 修改年龄为20    ((ObjectNode) rootNode).put("age", 20);    // 添加新的兴趣爱好:"旅行"    ((ArrayNode) rootNode.get("hobbies")).add("旅行");     // **合并**    // 假设有一个新的JSON片段要与原数据合并    String additionalJson = "{\"address\":\"北京市\",\"job\":\"程序员\"}";    JsonNode additionalNode = mapper.readTree(additionalJson);     // 使用ObjectNode#setAll方法将新节点中的键值对合并到原始节点中    ((ObjectNode) rootNode).setAll((ObjectNode) additionalNode);     // 打印更新后的JSON字符串    System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode));}//输出Age: 18First hobby: 写代码{  "name" : "小凡",  "age" : 20,  "hobbies" : [ "写代码", "看书", "打豆豆", "旅行" ],  "address" : "北京市",  "job" : "程序员"}
复制代码


九、Jackson 注解使用

9.1 序列化反序列化通用注解

9.1.1 @JsonIgnore


@JsonIgnore 用于在序列化或反序列化过程中忽略某个属性


① 定义实体类Person

public class Person {    private String name;    @JsonIgnore // 添加此注解以忽略 password 字段    private String password;    // 构造器、getter/setter 方法等...}
复制代码


使用示例

@Testpublic void testJsonIgnore() throws Exception{    Person person = new Person("小凡", "123456");    ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(person);}//输出{  "name": "小凡"}
复制代码


③ 详细解释


  • @JsonIgnore 注解直接放在属性声明前,如 private String password; 行上方。

  • 当 Person 类的对象被序列化为 JSON 时,password 属性将不会出现在生成的 JSON 字符串中

  • 同样地,在反序列化过程中,如果 JSON 数据中包含 password 字段,Jackson 在解析时会自动忽略它,不会尝试将其值设置到对应的 Person 对象属性上。


9.1.2 @JsonIgnoreProperties


@JsonIgnore 注解相比,@JsonIgnoreProperties 用于批量指定在序列化或反序列化过程中应忽略的属性列表,特别适用于应对不明确或动态变化的输入 JSON 中可能存在但不应处理的额外字段


① 定义实体类Person

希望同时忽略 password 和 socialSecurityNumber 两个敏感属性

@JsonIgnoreProperties({"password", "socialSecurityNumber"})public class Person {    private String name;    private String password;    private String socialSecurityNumber;     // 构造器、getter/setter 方法等...}
复制代码


② 使用示例

@Testpublic void testJsonIgnoreProperties() throws Exception{    Person person = new Person("小凡", "123456","233535");    ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(person);}//输出{  "name": "小凡"}
复制代码


③ 详细解释

  • @JsonIgnoreProperties 注解放置在类定义的开始处,作为类级别的注解。

  • 注解内通过一个字符串数组参数列举出要忽略的属性名。在这个例子中,{"password", "socialSecurityNumber"} 指定了 password 和 socialSecurityNumber 两个属性在序列化和反序列化时应被忽略。

  • 序列化 Person 对象时,生成的 JSON 字符串将不包含 password 和 socialSecurityNumber 属性。与前面 @JsonIgnore 示例类似,JSON 输出仅包含未被忽略的 name 属性:

{  "name": "小凡"}
复制代码


  • 反序列化时,即使输入的 JSON 数据中包含了 password 或 socialSecurityNumber 字段,Jackson 也会忽略它们,不会尝试将这些字段的值填充到相应的 Person 对象属性中。


④ @JsonIgnoreProperties 还有一个额外功能

@JsonIgnoreProperties有个可选的属性 ignoreUnknown,用于控制是否忽略 JSON 中存在但 Java 类中没有对应的未知属性。若设置为 true,则在反序列化时遇到未知属性时会自动忽略,避免抛出异常

@JsonIgnoreProperties(ignoreUnknown = true)public class Person {    // ...}
复制代码


9.1.3 @JsonIgnoreType


@JsonIgnoreType 注解在 Jackson 库中用于指示整个类在序列化或反序列化过程中应当被忽略。这适用于那些由于某种原因(如敏感信息、内部细节等)不需要或不应该被 JSON 化处理的类


定义CreditCardDetails 实体类

// 标记该类在序列化和反序列化时应被忽略@JsonIgnoreTypepublic class CreditCardDetails {    private String cardNumber;    private String cvv;    private String expirationDate;     // 构造器、getter/setter 方法等...}
复制代码


定义Customer 类


Customer 类,它有一个 creditCardDetails 属性引用上述 CreditCardDetails 类型的对象:

public class Customer {    private String customerId;    private String name;    private CreditCardDetails creditCardDetails;     // 构造器、getter/setter 方法等...}
复制代码


③ 使用示例

public void testJsonIgnoreType() throws JsonProcessingException {    Customer customer = new Customer("CUST001", "John Doe", new CreditCardDetails("1234567890123456", "123", "2024-12"));    ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(customer);}//输出{  "customerId": "CUST001",  "name": "John Doe"}
复制代码


④ 详细解释

  • @JsonIgnoreType 注解放置在需要忽略的类定义前,作为类级别的注解。

  • 当 Customer 类对象被序列化为 JSON 时,所有 CreditCardDetails 类型的属性(如 customer.creditCardDetails)都会被完全忽略,不会出现在生成的 JSON 字符串中。例如:

在此例中,生成的 json 字符串将不含 creditCardDetails 部分,仅包含 customerId 和 name

{  "customerId": "CUST001",  "name": "John Doe"}
复制代码


同理,在反序列化过程中,如果 JSON 数据中包含 CreditCardDetails 类型的嵌套结构,Jackson 解析时会自动忽略这部分内容,不会尝试创建或填充对应的 CreditCardDetails 对象


9.1.4 @JsonAutoDetect


@JsonAutoDetect 用于自定义类的属性(包括字段和 getter/setter 方法)在序列化和反序列化过程中的可见性规则。默认情况下,Jackson 只使用 public 的字段和 public 的 getter/setter 方法。通过使用 @JsonAutoDetect,你可以根据需要调整这些规则,以适应不同类的设计和数据模型。


① 定义实体类Employee

@JsonAutoDetect(    fieldVisibility = JsonAutoDetect.Visibility.NONE,   // 不使用字段    getterVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC, // 允许 protected 和 public 的 getter    setterVisibility = JsonAutoDetect.Visibility.ANY,   // 允许任意访问权限的 setter    isGetterVisibility = JsonAutoDetect.Visibility.DEFAULT,  // 默认行为(public)    creatorVisibility = JsonAutoDetect.Visibility.DEFAULT) // 默认行为(public)public class Employee {     protected String id;    String department; // package-private 字段     // 非标准命名的 getter    public String getIdentification() {        return id;    }     // 非标准命名的 setter    public void setIdentification(String id) {        this.id = id;    }     //...标准getter setter }
复制代码


使用示例

@Testpublic  void testJsonAutoDetect() throws JsonProcessingException {    ObjectMapper mapper = new ObjectMapper();     Employee employee = new Employee();    employee.setId("E001");    employee.setDepartment("Sales");     String json = mapper.writeValueAsString(employee);    System.out.println(json);}//输出{  "identification": "E001",  "department": "Sales"}
复制代码


③ 详细解释


  • 在上述 Employee 类的例子中:fieldVisibility = JsonAutoDetect.Visibility.NONE 指定不使用任何字段,意味着即使有 public 字段,也不会被 Jackson 处理。getterVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC 允许 Jackson 使用 protected 和 public 的 getter 方法。setterVisibility = JsonAutoDetect.Visibility.ANY 指定可以使用任意访问权限的 setter 方法。isGetterVisibility 和 creatorVisibility 使用默认行为,即只处理 public 的 is-getter 方法和构造函数。

  • 因为我们将 id 字段的 getter 和 setter 设为了非标准命名(getIdentification() 和 setIdentification()),且指定了 getterVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC,所以 Jackson 能正确识别并处理这两个方法,尽管它们不是标准的 get 前缀命名。


9.1.5 @JsonProperty


用于指定类的属性在序列化和反序列化成 JSON 时所对应的键名


Book 实体类

假设有一个 Book 类,其中包含 title 和 yearPublished 字段。我们希望在序列化和反序列化时,将 yearPublished 字段以 publishedYear 作为 JSON 键名。可以使用 @JsonProperty 注解进行重命名:

public class Book {    private String title;    @JsonProperty("publishedYear") // 重命名 yearPublished 字段为 publishedYear    private int yearPublished;    // 构造器、getter/setter 方法等...}
复制代码


使用示例:

现在创建一个 Book 对象并序列化为 JSON:

public void testJsonProperty() throws Exception {    Book book = new Book();    book.setTitle("小凡编程语录");    book.setYearPublished(1951);     ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(book);    System.out.println(json);}//输出{  "title": "小凡编程语录",  "publishedYear": 1951}
复制代码


详细解释

在本例中,@JsonProperty("publishedYear") 表明 yearPublished 字段在 JSON 中应称为 publishedYear

反序列化时,当遇到 JSON 中的 publishedYear 键时,Jackson 会知道应该将其值赋给 Book 类的 yearPublished 字段。


9.1.6 @JsonFormat


用于指定日期、时间、日期时间以及其他数值类型在序列化和反序列化为 JSON 时的格式


① 创建 Student 类

public class Student {    // 其他属性...     // 使用 @JsonFormat 注解的 LocalDate 属性    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")    private LocalDate birthday;    // 构造器、getter、setter 等...} 
复制代码


使用示例

注:Jackson 默认不支持 Java 8 的日期时间类型java.time.LocalDate,如果使用 jdk8 需要引入如下依赖.

<dependency>        <groupId>com.fasterxml.jackson.datatype</groupId>        <artifactId>jackson-datatype-jsr310</artifactId>        <version>最新版本号</version> <!-- 替换为当前最新的稳定版本 --></dependency>
复制代码


除了引入以来外,还需要确保在使用 Jackson 进行序列化和反序列化时,注册JavaTimeModule

mapper.registerModule(new JavaTimeModule());

@Testpublic void testJsonFormat() throws JsonProcessingException {    ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例    mapper.registerModule(new JavaTimeModule());     // 序列化示例    Student student = new Student();    student.setName("小凡");    student.setAge(18);    student.setBirthday(LocalDate.of(1990,1,1)); // 设置用户的出生日期    String json = mapper.writeValueAsString(student); // 将用户对象转换为 JSON 字符串    System.out.println(json); // 输出序列化后的 JSON 数据     // 反序列化示例    String inputJson = "{\"birthday\":\"1990-01-01\"}";    Student deserializedUser = mapper.readValue(inputJson, Student.class); // 从 JSON 字符串反序列化为 User 对象    System.out.println(deserializedUser.getBirthday()); // 输出反序列化后获得的出生日期}//输出{"name":"小凡","age":18,"birthday":"1990-01-01"}1990-01-01
复制代码


详细解释

@JsonFormat 注解在 Jackson 中被用来精确控制日期/时间类型的属性在 JSON 序列化和反序列化过程中的格式。通过给定合适的 shape 和 pattern 参数,可以确保日期数据在 Java 类型与 JSON 文本之间准确无误地转换。


9.2 序列化注解


9.2.1 @JsonInclude


用于控制对象在序列化过程中包含哪些属性。它可以防止空或特定值的字段被序列化到 JSON 输出中,从而帮助您精简 JSON 数据结构,减少不必要的传输量或避免向客户端暴露不必要的信息


定义PersonInclude实体类

@JsonInclude(JsonInclude.Include.NON_NULL)public class PersonInclude {     /**     * 姓名字段,由于未指定@JsonInclude注解,因此继承类级别的NON_NULL策略     */    private String name;     /**     * 职业字段,显式指定为仅在非空时才包含在序列化结果中     */    @JsonInclude(JsonInclude.Include.NON_EMPTY)    private String occupation;     /**     * 兴趣爱好列表,遵循类级别的NON_NULL策略     */    private List<String> hobbies;     // 构造函数、getter/setter等省略... }
复制代码


使用示例

@Testpublic void testJsonInclude() throws JsonProcessingException {    PersonInclude personFull = new PersonInclude();    personFull.setName("小凡");    personFull.setOccupation("程序员");    personFull.setHobbies(Arrays.asList("编程","看书","打豆豆"));     PersonInclude personPartial = new PersonInclude();    personPartial.setName(null);    personPartial.setOccupation("保洁员");      ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(personFull);    System.out.println(json);     String json1 = mapper.writeValueAsString(personPartial);    System.out.println(json1);  }//输出{"name":"小凡","occupation":"程序员","hobbies":["编程","看书","打豆豆"]}{"occupation":"保洁员"}
复制代码


③ 详细解释

  • 测试用例 1:序列化后,JSON 只包含nameoccupationhobbies字段都不为空,所以都显示出来

  • 测试用例 2:创建了一个部分字段为null或空的PersonInclude实例。为空的不显示


9.2.2 @JsonGetter


用于告诉 Jackson,应该通过调用 getter 方法而不是通过直接字段访问来获取某个字段值


创建PersonGetter实体类

public class PersonGetter {    private String firstName;    private String lastName;     // 使用 @JsonGetter 定义一个方法,该方法将在序列化时作为 "fullName" 属性的值返回    @JsonGetter("fullName")    public String getFullName() {        return this.firstName + " " + this.lastName;    }   // 构造函数和其他常规setter/getter方法}
复制代码


使用示例

@Testpublic void testJsonGetter() throws JsonProcessingException {    ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例     PersonGetter person = new PersonGetter("张", "小凡");// 创建一个 Person 对象     String json = mapper.writeValueAsString(person); // 将 Person 对象序列化为 JSON 字符串     System.out.println(json); // 输出序列化后的 JSON}//输出{"firstName":"张","lastName":"小凡","fullName":"张 小凡"}
复制代码


@JsonGetter 注解成功地将 getFullName() 方法的结果映射到 JSON 对象中的 "fullName" 属性。


9.2.3 @JsonAnyGetter


用于标记一个方法,获取除已知属性外的所有其他键值对。这些键值对通常存储在一个 Map 结构中,

以便将它们作为一个附加的对象进行序列化。


① 创建CustomData实体类

@Datapublic class CustomData {    private final Map<String, Object> additionalProperties = new HashMap<>();     // 使用 @JsonAnyGetter 定义一个方法,该方法将在序列化时返回所有额外属性    @JsonAnyGetter    public Map<String, Object> getAdditionalProperties() {        return additionalProperties;    }     // 提供方法来添加或修改额外属性    public void addProperty(String key, Object value) {        additionalProperties.put(key, value);    }}
复制代码


使用示例

 @Testpublic void testJsonAnyGetter() throws JsonProcessingException {    ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例     CustomData data = new CustomData();    data.addProperty("key1", "value1");    data.addProperty("key2", 42);    data.addProperty("key3", "value3");     String json = mapper.writeValueAsString(data); // 将 CustomData 对象序列化为 JSON 字符串     System.out.println(json); // 输出序列化后的 JSON}//输出{"key1":"value1","key2":42,"key3":"value3"}
复制代码


@JsonAnyGetter 注解的主要作用如下:

  • 动态属性支持:通过在返回 Map 的方法上使用 @JsonAnyGetter,您可以将一个对象的动态属性集合序列化为 JSON 对象的多个键值对。这些属性可能是在运行时添加的,或者基于某些条件动态生成的。

  • 简化结构:避免为每个可能的动态属性单独声明字段和 getter/setter。只需维护一个 Map,即可处理任意数量和类型的额外属性。

  • 兼容性与灵活性:当需要与未知或未来可能变化的数据结构交互时,@JsonAnyGetter 可确保 JSON 表示能够容纳未预定义的属性,从而提高系统的兼容性和适应性。


9.2.4 @JsonPropertyOrder


用于指定类中属性在序列化为 JSON 时的排序规则


创建OrderPerson 实体类

public class OrderPerson {    @JsonProperty("firstName")    private String givenName;     @JsonProperty("lastName")    private String familyName;    // 构造函数和其他常规setter/getter方法}
复制代码


使用示例

 @Testpublic void testJsonPropertyOrder() throws JsonProcessingException {    ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例     OrderPerson person = new OrderPerson("John", "Doe"); // 创建一个 OrderedPerson 对象     String json = mapper.writeValueAsString(person); // 将 OrderedPerson 对象序列化为 JSON 字符串     System.out.println(json); // 输出序列化后的 JSON}//输出{"firstName":"John","lastName":"Doe"}
复制代码


@JsonPropertyOrder 注解按照指定顺序("lastName""firstName")对 JSON 对象的属性进行了排序。


9.2.5 @JsonRawValue


用于标记一个字段或方法返回值,指示 Jackson 在序列化时应将其原始值视为未经转义的 JSON 字符串,并直接嵌入到输出的 JSON 文档中。即使所标记的值包含 JSON 特殊字符(如双引号、反斜杠等),也不会对其进行转义


① 创建一个包含 @JsonRawValue 注解的类

public class RawJsonValueExample {    private String normalProperty = "这是一个正常字符串";     // 使用 @JsonRawValue 标记 rawJsonProperty,使其内容被视为未经转义的 JSON 值    @JsonRawValue    private String rawJsonProperty = "{\"key\": \"value\", \"array\": [1, 2, 3]}";     // 构造函数和其他常规setter/getter方法}
复制代码


使用示例

@Testpublic void testJsonRawValue() throws JsonProcessingException {    ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例     RawJsonValueExample example = new RawJsonValueExample(); // 创建一个 RawJsonValueExample 对象     String json = mapper.writeValueAsString(example); // 将 RawJsonValueExample 对象序列化为 JSON 字符串     System.out.println(json); // 输出序列化后的 JSON}//输出{"normalProperty":"这是一个正常字符串","rawJsonProperty":{"key": "value", "array": [1, 2, 3]}}
复制代码


rawJsonProperty 的值并未被转义,而是作为一个完整的 JSON 对象直接嵌入到父 JSON 文档中。

当您需要在 JSON 对象中嵌入另一段 JSON 文本时,使用 @JsonRawValue 可以确保这段文本以未经转义的形式出现在输出的 JSON 中,保持其原有的 JSON 结构。


9.2.6 @JsonValue


用于标记一个方法或字段,Jackson 在序列化该类实例时,直接使用该方法的返回值或字段的值作为整个对象的 JSON 表示,而非按照类的常规属性进行序列化


① 创建JsonValueExample

public class JsonValueExample {    private LocalDate date;    private String formattedDate;     @JsonValue    public String asJsonString() {        return "{\"date\":\"" + date.toString() + "\",\"formatted\":\"" + formattedDate + "\"}";    }    // 构造函数和其他常规setter/getter方法}
复制代码


② 使用示例

@Testpublic void testJsonValue() throws JsonProcessingException {    ObjectMapper mapper = new ObjectMapper();     JsonValueExample example = new JsonValueExample();    example.setDate(LocalDate.of(2024,4,6));    example.setFormattedDate("yyyy-MM-dd");     String json = mapper.writeValueAsString(example);     System.out.println(json);}//输出"{\"date\":\"2024-04-06\",\"formatted\":\"yyyy-MM-dd\"}"
复制代码


在 asJsonString() 方法上应用注解,指示序列化时应将该方法的返回值作为整个对象的 JSON 表达,忽略类中其他的属性


9.2.7 @JsonSerialize


于指定类、字段或方法在序列化过程中使用的自定义序列化逻辑


① 创建一个包含 @JsonSerialize 注解的类及其自定义序列化器

public class BooleanSerializer extends JsonSerializer<Boolean> {    @Override    public void serialize(Boolean aBoolean, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {        if(aBoolean){            jsonGenerator.writeNumber(1);        } else {            jsonGenerator.writeNumber(0);        }    }} public class PersonSerializer {    public long   personId = 0;    public String name     = "John";     @JsonSerialize(using = BooleanSerializer.class)    public boolean enabled = false;    // 构造函数和其他常规setter/getter方法} 
复制代码


使用示例

@Testpublic void testJsonSerialize() throws JsonProcessingException {    PersonSerializer person = new PersonSerializer();    person.setName("小凡");    person.setPersonId(1001);    person.setEnabled(true);     ObjectMapper mapper = new ObjectMapper();    String json = mapper.writeValueAsString(person);    System.out.println(json);}//输出{"personId":1001,"name":"小凡","enabled":1}
复制代码


通过上面代码测试我们可以看到,enabled 被序列化为 1

9.3 反序列化注解

9.3.1 @JsonSetter


用于标记一个方法或字段,指示 Jackson 在反序列化过程中应如何设置该属性的值


创建PersonSetter 类

public class PersonSetter {    @JsonSetter("first_name")    private String firstName;    @JsonSetter("last_name")    private String lastName;  // 其他getter setter toString方法省略...}
复制代码


使用示例

@Testpublic void testJsonSetter() throws JsonProcessingException {     String jsonNormal = "{\"first_name\":\"张\", \"last_name\":\"小凡\"}";    ObjectMapper mapper = new ObjectMapper();    PersonSetter person = mapper.readValue(jsonNormal, PersonSetter.class);    System.out.println(person);}//输出PersonSetter(firstName=张, lastName=小凡)
复制代码


@JsonSetter 注解提供了灵活的方式来定制 Jackson 在反序列化期间如何将 JSON 数据映射到 Java 对象的属性


9.3.2 @JsonAnySetter


用于处理反序列化过程中遇到的未知或额外的 JSON 键值对。当一个对象的 JSON 表示中包含无法直接映射到已声明属性的键时,这个注解可以帮助捕获并存储这些额外的数据


① 定义带有 @JsonAnySetter 注解的类

@ToStringpublic class CustomObject {    private String knownProperty;    private Map<String, Object> additionalProperties = new HashMap<>();     // 已知属性的setter方法    public void setKnownProperty(String knownProperty) {        this.knownProperty = knownProperty;    }     // 获取已知属性的getter方法省略...     /**     * @JsonAnySetter注解方法,用于处理反序列化过程中遇到的所有未知属性。     *     * @param key   未知属性的键     * @param value 未知属性的值     */    @JsonAnySetter    public void addAdditionalProperty(String key, Object value) {        additionalProperties.put(key, value);    }     // 提供访问额外属性的方法    public Map<String, Object> getAdditionalProperties() {        return additionalProperties;    }}
复制代码


使用示例

@Testpublic void testJsonAnySetter() throws JsonProcessingException {    String json ="{\"knownProperty\":\"expectedValue\",\"extraField1\":\"someValue\",\"extraField2\":42,\"nestedObject\":{\"key\":\"value\"}}";     ObjectMapper mapper = new ObjectMapper();     CustomObject customObject = mapper.readValue(json, CustomObject.class);    System.out.println(customObject);}//输出CustomObject(knownProperty=expectedValue, additionalProperties={extraField1=someValue, nestedObject={key=value}, extraField2=42})
复制代码


@JsonAnySetter注解来应对 JSON 反序列化过程中可能出现的未知属性,确保所有数据都能被妥善处理和保留。这种机制特别适用于需要兼容动态或扩展性较强的 JSON 输入场景。


9.3.3 @JsonCreator


用于标记一个构造器、静态工厂方法或实例方法,使其成为反序列化过程中创建对象实例的入口点。这个注解帮助 Jackson 确定如何根据 JSON 数据构建相应的 Java 对象。


① 定义带有 @JsonCreator 注解的类

public class AddressCreator {    private  String street;    private  int number;    private  String city;     // 构造器上使用@JsonCreator注解,指示Jackson使用此构造器反序列化JSON    @JsonCreator    public AddressCreator(@JsonProperty("street") String street,                   @JsonProperty("number") int number,                   @JsonProperty("city") String city) {        this.street = street;        this.number = number;        this.city = city;    }    // getter setter toString方法省略...}
复制代码


② 使用示例

@Testpublic void testCreateor() throws JsonProcessingException {    String json ="{\"street\":\"呈贡区\",\"number\":123,\"city\":\"昆明\"}";    ObjectMapper mapper = new ObjectMapper();    AddressCreator addressCreator = mapper.readValue(json, AddressCreator.class);    System.out.println(addressCreator);}//输出AddressCreator(street=呈贡区, number=123, city=昆明)
复制代码


9.3.4 @JacksonInject


用于在反序列化过程中自动注入依赖项或附加信息到目标对象。通常用于处理那些不能直接从 JSON 数据中获取、但又希望在反序列化完成后立即可用的信息


① 定义带有 @JacksonInject 注解的类

public class UserInject {    private String name;    private String email;     // @JacksonInject注解在字段上,指示Jackson在反序列化过程中注入特定值    @JacksonInject("defaultEmail")    private String defaultEmail;     // 构造器和getter setter toString方法省略...}
复制代码


使用示例

@Testpublic void testJacksonInject() throws JsonProcessingException {    // 设置可注入的值    InjectableValues injectables = new InjectableValues.Std().addValue("defaultEmail", "xiaofan@example.com");     // 创建ObjectMapper并配置注入值    ObjectMapper mapper = new ObjectMapper().setInjectableValues(injectables);     String json = "{\"name\":\"小凡\"}";     // 反序列化时,defaultEmail字段会被自动注入指定值    UserInject userInject = mapper.readValue(json, UserInject.class);     System.out.println("Name: " + userInject.getName());    System.out.println("Email: " + userInject.getEmail());    System.out.println("Default Email: " + userInject.getDefaultEmail());}//输出Name: 小凡Email: nullDefault Email: xiaofan@example.com
复制代码


@JacksonInject 注解在 Jackson 反序列化过程中用于引入外部依赖或默认值,使得这些信息能够在反序列化完成后立即可用


9.3.5 @JsonDeserialize


用于在反序列化过程中指定自定义的反序列化器来处理某个字段或类的特殊逻辑


自定义反序列化器

public class BooleanDeserializer extends JsonDeserializer<Boolean> {    @Override    public Boolean deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {        String text = jsonParser.getText();        if("0".equals(text)) {            return false;        }        return true;    }}
复制代码


② @JsonDeserialize注解添加到要为其使用自定义反序列化器的字段

public class PersonDeserialize {    public long    id;    public String  name;     @JsonDeserialize(using = BooleanDeserializer.class)    public boolean enabled = false;    //构造器和getter setter toString方法省略...}
复制代码


使用示例

{"id":1001,"name":"小凡","enabled":1}
复制代码


@Testpublic void testJsonDeserialize() throws JsonProcessingException {    String json ="{\"id\":1001,\"name\":\"小凡\",\"enabled\":1}";    ObjectMapper mapper = new ObjectMapper();    PersonDeserialize personDeserialize = mapper.readValue(json, PersonDeserialize.class);    System.out.println(personDeserialize);}//输出PersonDeserialize(id=1001, name=小凡, enabled=true)
复制代码


我们 json 字符串中 enabled 的值是 1,最终反序列化成PersonDeserialize 对象后值变成了ture

本期内容到此就结束了,希望对你有所帮助。我们下期再见 (●'◡'●)


文章转载自:xiezhr

原文链接:https://www.cnblogs.com/xiezhr/p/18119893

体验地址:http://www.jnpfsoft.com/?from=001


用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
轻松上手Jackjson(珍藏版)_Python_不在线第一只蜗牛_InfoQ写作社区