Jackson 是一个流行的库,用于将 Java 对象序列化/反序列化为各种文本格式。ObjectMapper类是该库处理 JSON 格式的主要方式对于其他格式,我们有它的后代(XmlMapperYAMLMapper)。由于继承,我们可以通过单一界面以一致的方式处理所有格式。

下载 jar 文件

在学习示例之前,我们需要下载 Jackson jar 文件并将其连接到 IntelliJ IDEA 中的项目。下面以jackson-databind为例,看看如何搜索需要的文件:

  1. 转到Maven 存储库网站。

  2. 在搜索框中输入“ jackson-databind ”。您将获得以下内容:

  3. 第一个搜索结果是我们感兴趣的。点击链接。

  4. 有时可能需要特定版本的库来确保与项目中其他组件的兼容性。最新版本应该可以工作(在编写本课程时,它是 2.13.2.2)。点击链接。

  5. 在打开的页面上,您需要“捆绑包”链接:

  6. 使用此链接下载 jar 文件。

按照类似的过程,您可以找到并下载其余必要的 jar 文件:

下载所有必要的文件后,将它们连接到 IntelliJ IDEA 中的项目:

  1. 打开项目设置(您可以使用Ctrl+Alt+Shift+S组合键)。

  2. 图书馆

  3. +,然后按“Java”。选择所有下载的文件。这是我们最终应该得到的:

  4. 我们的准备工作到此结束。现在我们可以试用ObjectMapper了。

序列化为 JSON

首先,让我们将一些对象序列化为 JSON:


import com.fasterxml.jackson.databind.ObjectMapper;
 
class Book {
	public String title;
	public String author;
	public int pages;
}
 
public class Solution {
	public static void main(String[] args) throws Exception {
    	Book book = new Book();
    	book.title = "Good Omens";
    	book.author = "Pratchett T., Gaiman N.";
    	book.pages = 383;
 
    	ObjectMapper mapper = new ObjectMapper();
    	String jsonBook = mapper.writeValueAsString(book);
    	System.out.println(jsonBook);
	}
}

运行main会给你这个输出:

{"title":"Good Omens","author":"Pratchett T., Gaiman N.","pages":383}

ObjectMapper很多高级设置。让我们使用其中之一来使 JSON 字符串更具可读性。创建后对象映射器对象,执行这条语句:

mapper.enable(SerializationFeature.INDENT_OUTPUT);

输出中的信息保持不变,但现在有缩进和换行符:

{
  “标题”:“好兆头”,
  “作者”:“普拉切特 T.,盖曼 N.”,
 “页数”:383
}

从 JSON 反序列化

现在让我们做相反的动作:我们将一个字符串反序列化为一个对象。为了能够看到程序在做什么,让我们重写Book类中的toString方法:


@Override
public String toString() {
	return "Book{" +
        	"title='" + title + '\'' +
        	", author='" + author + '\'' +
        	", pages=" + pages +
        	'}';
}

我们将在main方法中执行以下操作:


public static void main(String[] args) throws Exception {
	String jsonString = "{\"title\":\"Good Omens\",\"author\":\"Pratchett T., Gaiman N.\",\"pages\":383}";
	Book book = new ObjectMapper().readValue(jsonString, Book.class);
	System.out.println(book);
}

输出:

Book{title='Good Omens', author='Pratchett T., Gaiman N.', pages=383}

readValue方法被重载了——它有很多变体,可以接受一个文件、一个链接、各种输入流等。为简单起见,我们的示例使用了一个接受 JSON 字符串的变体。

如上所述,ObjectMapper有很多设置。让我们来看看其中的几个。

忽略未知属性

考虑这样一种情况,JSON 字符串具有Book类中不存在的属性:


public static void main(String[] args) throws Exception {
	String jsonString = """
        	{
          	"title" : "Good Omens",
          	"author" : "Pratchett T., Gaiman N.",
          	"pages" : 383,
          	"unknown property" : 42
        	}""";
	ObjectMapper mapper = new ObjectMapper();
	Book book = mapper.readValue(jsonString, Book.class);
	System.out.println(book);
}

执行这段代码给我们一个UnrecognizedPropertyException。这是默认行为,但我们可以更改它:


ObjectMapper mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

创建时对象映射器对象,我们使用configure方法将相应的设置设置为false。configure方法修改调用它的对象,然后返回同一个对象,所以我们可以用不同的方式进行调用


ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

就功能而言,此表示法与前一个表示法一样。

如果我们现在运行main方法,反序列化将成功,未知属性将被忽略。

方便的注释

Jackson 为我们提供了几个注释,允许我们以各种方式自定义序列化过程。让我们来看看一些最有用的:

@JsonIgnore — 此注释位于序列化/反序列化期间应忽略的元素上方:


class Book {
	public String title;
	@JsonIgnore
	public String author;
	public int pages;
}

这里的作者在序列化期间,字段不会包含在生成的 JSON 中。反序列化后,作者字段将获得默认值 (null),即使 JSON 具有不同的值。

@JsonFormat — 此注释允许您设置序列化数据的格式。让我们在Book类中再添加一个字段Date


class Book {
	public String title;
	public String author;
	public int pages;
	public Date createdDate = new Date();
}

序列化后,我们得到以下 JSON:

 {
  “标题”:“好兆头”,
  “作者”:“Pratchett T.,Gaiman N.”,
  “页面”:383,
  “createdDate”:1649330880788
}

如您所见,日期被序列化为数字。我们将添加注释并设置格式:


class Book {
	public String title;
	public String author;
	public int pages;
	@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
	public Date createdDate = new Date();
}

现在序列化的结果是:

{
  “标题”:“好兆头”,
  “作者”:“Pratchett T.,Gaiman N.”,
  “页面”:383,
  “createdDate”:“2022-04-07”
}

@JsonProperty — 此注释允许您更改表示序列化字段的属性的名称。您还可以使用此注释标记方法。如果这样做,那么它们的返回值将在序列化期间转换为 JSON 属性:


class Book {
	@JsonProperty("name")
	public String title;
	public String author;
	public int pages;
 
	@JsonProperty("quotedTitle")
	public String getQuotedTitle() {
    	    return "\"" + title + "\"";
	}
}

序列化结果:

{
  "author" : "Pratchett T., Gaiman N.",
  "pages" : 383,
  "name" : "Good Omens",
  "quotedTitle" : "\"Good Omens\""
}

@JsonInclude — 使用此注释,您可以指定序列化字段必须满足的条件。您可以将其应用于单个领域或整个班级。首先,让我们尝试序列化一个带有未初始化字段的对象:


public class Solution {
	public static void main(String[] args) throws Exception {
    		Book book = new Book();

    		ObjectMapper mapper = new ObjectMapper();
    		mapper.enable(SerializationFeature.INDENT_OUTPUT);
    		String jsonBook = mapper.writeValueAsString(book);
    		System.out.println(jsonBook);
	}
}

序列化结果:

{
  “标题”:空,
  “作者”:空,
  “页面”:0
}

如果您添加注释:


@JsonInclude(JsonInclude.Include.NON_NULL)
class Book {
	public String title;
	public String author;
	public int pages;
}

然后我们得到这个结果:

{
  “页面”:0
}

现在不序列化为null的字段。

@JsonPropertyOrder — 此注释允许您设置字段序列化的顺序:


@JsonPropertyOrder({"author", "title", "pages"})
class Book {
	public String title;
	public String author;
	public int pages;
}

序列化结果:

{
  "author" : "Pratchett T., Gaiman N.",
  "title" : "Good Omens",
  "pages" : 383
}

现在,只需记住如何使用注释。在本模块结束时,我们将更好地了解它们,甚至创建我们自己的注释。

XML 中的序列化和反序列化

如果我们需要序列化为 XML,我们可以使用所有相同的设置和注释。唯一的区别是映射器对象:


public static void main(String[] args) throws Exception {
	Book book = new Book();
	book.title = "Good Omens";
	book.author = "Pratchett T., Gaiman N.";
	book.pages = 383;
 
	ObjectMapper mapper = new XmlMapper();
	mapper.enable(SerializationFeature.INDENT_OUTPUT);
	String xmlBook = mapper.writeValueAsString(book);
	System.out.println(xmlBook);
}

输出:

 <Book>
  <title>好兆头</title>
  <author>Pratchett T., Gaiman N.</author>
  <pages>383</pages>
</Book>

XML反序列化:


public static void main(String[] args) throws Exception {
   String xmlString = """
            <Book>
             <title>Good Omens</title>
             <author>Pratchett T., Gaiman N.</author>
             <pages>383</pages>
           </Book>""";
   ObjectMapper mapper = new XmlMapper();
   Book book = mapper.readValue(xmlString, Book.class);
   System.out.println(book);
}

YAML 中的序列化和反序列化

我们像处理 XML 一样处理 YAML:


public static void main(String[] args) throws Exception {
	Book book = new Book();
	book.title = "Good Omens";
	book.author = "Pratchett T., Gaiman N.";
	book.pages = 383;
 
	ObjectMapper mapper = new YAMLMapper();
	mapper.enable(SerializationFeature.INDENT_OUTPUT);
	String yamlBook = mapper.writeValueAsString(book);
	System.out.println(yamlBook);
}

输出:

---
标题:“好兆头”
作者:“Pratchett T., Gaiman N.”
页数:383

YAML 的反序列化:


public static void main(String[] args) throws Exception {
   String yamlString = """
           ---
           title: "Good Omens"
           author: "Pratchett T., Gaiman N."
           pages: 383""";
   ObjectMapper mapper = new YAMLMapper();
   Book book = mapper.readValue(yamlString, Book.class);
   System.out.println(book);
}