• 完全理解Gson(3):Gson反序列化


    本文延续前一篇文章,继续介绍简单基本的Gson用法。这篇文章我们将介绍如何将复杂的JSON对象解析为Java对象,其中Java对象的结构可以与JSON对象不一致。我们还会看到如何使用Gson反序列化器(JsonDeserializer Java文档)将JSON对象映射为Java对象。

    观察

    请注意文中解析(parse)与反序列化(deserialise)将互换使用。

    下面列出的所有代码都可以在 https://java-creed-examples.googlecode.com/svn/gson/Gson Deserialiser Example获取。绝大多数例子都不包含全部代码,与讨论主题不相关的代码片段有可能被忽略。读者可以从上面的链接下载或阅读全部代码。

    对于不熟悉Gson的读者,鼓励您先阅读简单Gson用例 ,熟悉之后再开始阅读本篇内容。

    一个简单的实例

    比方说,我们有如下JSON对象,它包含两位著名作者的畅销Java书(Amazon)。

    1
    2
    3
    4
    5
    6
    {
      'title':    'Java Puzzlers: Traps, Pitfalls, and Corner Cases',
      'isbn-10''032133678X',
      'isbn-13''978-0321336781',
      'authors':  ['Joshua Bloch', 'Neal Gafter']
    }

    上面的JSON对象包括4个字段,其中一个是数组。这些字段代表了我们的书籍。使用简单Gson实例中讨论的方法可能产生一个问题。默认情况下,Gson期望Java类中的变量名与JSON查找到的名称一样。因此,我们需要包含如下域名的类:titleisbn-10isbn-13authors。但是Java语言规范 (第六章)指出,Java变量名不能包含减号(-)。

    我们将在接下来的实例中看到如何使用JsonDeserializer完全控制JSON的解析。另外我们也可以使用Gson注解实例中提到的注解。注解控制JSON解析的能力稍弱,但是使用简单便于理解。当然,注解也有它们的限制,不能解决这里提到的所有问题。

    考虑下面简单的Java对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.javacreed.examples.gson.part1;
     
    public class Book {
     
      private String[] authors;
      private String isbn10;
      private String isbn13;
      private String title;
     
      // Methods removed for brevity
    }

    Java对象用来存储之前JSON对象中的书籍信息。注意,JSON对象有4个字段,每个变量对应一个JSON字段。这两个对象(Java和JSON)的结构不必一致。Java对象的结构可以与JSON对象不同。

    为了将JSON对象解析成Java对象,我们需要创建自己的 JsonDeserializer接口实例,并且注册到GsonBuilderJava文档)中。下面的例子展示了我们实现的 JsonDeserializer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package com.javacreed.examples.gson.part1;
     
    import java.lang.reflect.Type;
     
    import com.google.gson.JsonArray;
    import com.google.gson.JsonDeserializationContext;
    import com.google.gson.JsonDeserializer;
    import com.google.gson.JsonElement;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParseException;
     
    public class BookDeserializer implements JsonDeserializer<Book> {
     
      @Override
      public Book deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
          throws JsonParseException {
     
        //The deserialisation code is missing
     
        final Book book = new Book();
        book.setTitle(title);
        book.setIsbn10(isbn10);
        book.setIsbn13(isbn13);
        book.setAuthors(authors);
        return book;
      }
    }

    上面的例子是不完整的,我们还需要增加最重要的部分——反序列化。在增加更多代码之前,我们先了解一下这个类变复杂之前的版本。

    JsonDeserializer接口需要一个类型,该类型是需要我们解析的对象类型。在这个例子里,我们将JSON对象解析成 Book类型的Java对象。 deserialize()方法的返回类型必须与泛型参数一致,为Book类型。

    Gson将JSON对象解析成一个JsonElementJava文档)类型的Java对象。一个 JsonElement实例可以是下面类型之一:

    • JsonPrimitiveJava Doc):例如一个字符串或整数。
    • JsonObjectJava文档):JsonElement的集合,以名称(String类型)为索引。 与Map<String, JsonElement> (Java文档)相似。
    • JsonArray (Java文档):JsonElement的集合。注意数组元素可以是任何4中类型,也支持混合类型。
    • JsonNullJava文档):null值。
    JsonElement的类型

    上图显示了所有 JsonElement的类型。 JsonObject可以被认为是一个键值对的集合,其中值是JsonElement类型。因此,该值可以是其他对象。

     
    Json对象层级

    上图以 JsonObject 为根展示了一个JSON对象层级。特别需要注意,不同于Java,JSON支持不同类型的数组。上图中, JsonArray 包含JsonObject、JsonArrayJsonPrimitive。请注意,上图展示的JSON对象层级不反映前面列出的JSON对象。下面前面列出的JSON对象的JSON对象层级。

    Book Json对象层级

    如果我们反序列化这个JSON对象,首先需要将给定的 JsonElement转换为一个 JsonObject

    1
    2
    // The variable 'json' is passed as a parameter to the deserialize() method
    final JsonObject jsonObject = json.getAsJsonObject();

    使用相似的方法,JsonElement可以转换成其他任何类型。

    JsonObject中的元素可以使用名称进行检索。例如,要从上面列出的JSON对象检索title元素,我们可以进行下面操作。

    1
    2
    3
    // The variable 'json' is passed as a parameter to the deserialize() method
    final JsonObject jsonObject = json.getAsJsonObject();
    JsonElement titleElement = jsonObject.get("title")

    返回的对象不是一个 String,而是另一个 JsonElement。可以调用 getAsString() 方法将 JsonElement转换为 String ,代码如下:

    1
    2
    3
    4
    // The variable 'json' is passed as a parameter to the deserialize() method
    final JsonObject jsonObject = json.getAsJsonObject();
    JsonElement titleElement = jsonObject.get("title")
    final String title = jsonTitle.getAsString();

    下面的例子展示了如何使用定制的反序列化器转换上面列出的JSON对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    package com.javacreed.examples.gson.part1;
     
    import java.lang.reflect.Type;
     
    import com.google.gson.JsonArray;
    import com.google.gson.JsonDeserializationContext;
    import com.google.gson.JsonDeserializer;
    import com.google.gson.JsonElement;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParseException;
     
    public class BookDeserializer implements JsonDeserializer<Book> {
     
      @Override
      public Book deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
          throws JsonParseException {
        final JsonObject jsonObject = json.getAsJsonObject();
     
        final JsonElement jsonTitle = jsonObject.get("title");
        final String title = jsonTitle.getAsString();
     
        final String isbn10 = jsonObject.get("isbn-10").getAsString();
        final String isbn13 = jsonObject.get("isbn-13").getAsString();
     
        final JsonArray jsonAuthorsArray = jsonObject.get("authors").getAsJsonArray();
        final String[] authors = new String[jsonAuthorsArray.size()];
        for (int i = 0; i < authors.length; i++) {
          final JsonElement jsonAuthor = jsonAuthorsArray.get(i);
          authors[i] = jsonAuthor.getAsString();
        }
     
        final Book book = new Book();
        book.setTitle(title);
        book.setIsbn10(isbn10);
        book.setIsbn13(isbn13);
        book.setAuthors(authors);
        return book;
      }
    }

    上例中,我们检索JSON元素和它的4个字段,并返回了一个 Book实例。

    在可以使用新的反序列化器之前,必须指定Gson使用我们的反序列化器来解析 Book类型的对象,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.javacreed.examples.gson.part1;
     
    import java.io.InputStreamReader;
    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
     
    public class Main {
      public static void main(String[] args) throws Exception {
        // Configure Gson
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(Book.class, new BookDeserializer());
        Gson gson = gsonBuilder.create();
     
        // The JSON data
        try(Reader reader = new InputStreamReader(Main.class.getResourceAsStream("/part1/sample.json"), "UTF-8")){
     
          // Parse JSON to Java
          Book book = gson.fromJson(reader, Book.class);
          System.out.println(book);
        }
      }
    }

    上例中,我们通过 GsonBuilder创建了一个 Gson实例。使用 registerTypeAdapter()方法向Gson注册了我们的反序列化器,并指定反序列化 Book类型对象时使用我们定义的反序列化器。当请求Gson反序列化一个 Book类对象时,Gson将使用我们定义的反序列化器。下面的步骤描述了我们调用 gson.fromJson(data, Book.class)时发生了什么。

    1.   将输入解析成 JsonElement对象。注意,即使对象的类型是 JsonElement,输入可以是任何类型。在这个阶段,JSON对象字符串被反序列化为 JsonElement类型的Java对象。这个步骤还确保给定JSON数据的有效性。
    2.   检索给定对象的反解析器,本例中是 BookDeserializer实例。
    3.   调用 deserialize()方法并提供必需的参数。例子里,将调用我们的 deserialize()方法。这里将从给定的 JsonElement对象创建一个 Book类型对象。这是Java内部的转化。
    4.   返回 deserialize()方法的返回值到调用者 fromJson()方法。这一步像一个链条,Gson从我们的反序列化器接收一个对象并返回给它的调用者。

    执行上面的例子可能得到下面的输出:

    1
    2
    3
    4
    5
    Java Puzzlers: Traps, Pitfalls, and Corner Cases
      [ISBN-10: 032133678X] [ISBN-13: 978-0321336781]
    Written by:
      >> Joshua Bloch
      >> Neal Gafter

    至此我们结束了简单的例子。本例是后续更复杂例子的引子。在下一个例子里,我们将讨论当前对象的一个增强版本,该版本的作者对象不仅是一个简单的字符串,而是一个对象。

    嵌套对象

    本例中,我们将描述如何反序列化嵌套对象,也就是对象包含对象。这里,我们将介绍一个新的实体,作者。一本书,除了有标题和ISBN号,还可以有多个作者。换句话说,每个作者可以写多本书。为了增加新的实体,本例中的JSON对象做了修改,与前例不同:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
      'title': 'Java Puzzlers: Traps, Pitfalls, and Corner Cases',
      'isbn': '032133678X',
      'authors':[
        {
          'id': 1,
          'name': 'Joshua Bloch'
        },
        {
          'id': 2,
          'name': 'Neal Gafter'
        }
      ]
    }

    稍微调整了JSON对象的结构并用JSON对象的作者数组代替了之前的原型,如下图:

    Book与Authors Json对象层级

    我们还是以一本书为例,只是这次我们有了更复杂和更详细的JSON对象。除了 name字段,作者对象还有一个 id字段。为这个模型增加了新类称为 Author, Book类用它保存作者信息。这立即导致如下问题。

    如何反序列化新的 Author 类?

    这里有几种选择。

    1. 我们可以更新 BookDeserializer 并增加反解析作者信息的代码。这有个限制,它将 Author 的反序列化与 Book绑定了,因此不推荐这个方法。
    2. 我们可以使用默认的Gson实现,该方法在这个例子中工作正常,因为Java对象(Author类)和JSON对象有同名的字段,可以进行简单Gson实例文中提到的反序列化。
    3.  或者,我们可以写一个 AuthorDeserializer 类,该类会处理Author的反序列化。

    我们从第二种选择开始,保证改变最小化,例子尽量简单。然后我们增加新的反序列化器来展示Gson的灵活性。

    JsonDeserializer提供了一个 JsonDeserializationContextJava文档)实例作为deserialize()方法的第三个参数。我们还没有用过这个参数。我们可以将对象的反序列化委托给指定的 JsonDeserializationContext实例。它将反序列化给定的 JsonElement 并返回一个指定类型的实例,代码如下。

    1
    Author author = context.deserialize(jsonElement, Author.class);

    上例将 Author类的反序列化委托给 context变量。反过来,它试图搜索已注册的可以反序列化 Author类的 JsonDeserialize实例,如果未发现注册的实例,它将使用简单Gson实例中提到的默认机制。

    我们的例子使用了一个 Author数组,因此我们需要使用正确的类型,例子如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package com.javacreed.examples.gson.part2;
     
    import java.lang.reflect.Type;
     
    import com.google.gson.JsonArray;
    import com.google.gson.JsonDeserializationContext;
    import com.google.gson.JsonDeserializer;
    import com.google.gson.JsonElement;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParseException;
     
    public class BookDeserializer implements JsonDeserializer<Book> {
     
      @Override
      public Book deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
          throws JsonParseException {
       final JsonObject jsonObject = json.getAsJsonObject();
     
        final String title = jsonObject.get("title").getAsString();
        final String isbn10 = jsonObject.get("isbn-10").getAsString();
        final String isbn13 = jsonObject.get("isbn-13").getAsString();
     
        // Delegate the deserialization to the context
        Author[] authors = context.deserialize(jsonObject.get("authors"), Author[].class);
     
        final Book book = new Book();
        book.setTitle(title);
        book.setIsbn10(isbn10);
        book.setIsbn13(isbn13);
        book.setAuthors(authors);
        return book;
      }
    }

    从 JsonPrimitive 转换成 JsonObject 是十分简单直接的,就像上面例子中看到的那样。

    BookDeserialiser类似,我们可以编写 ArthurDeserialiser类并使用处理书籍相似的方式反序列化作者。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package com.javacreed.examples.gson.part2;
     
    import java.lang.reflect.Type;
     
    import com.google.gson.JsonDeserializationContext;
    import com.google.gson.JsonDeserializer;
    import com.google.gson.JsonElement;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParseException;
     
    public class AuthorDeserializer implements JsonDeserializer {
     
      @Override
      public Author deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
          throws JsonParseException {
        final JsonObject jsonObject = json.getAsJsonObject();
     
        final Author author = new Author();
        author.setId(jsonObject.get("id").getAsInt());
        author.setName(jsonObject.get("name").getAsString());
        return author;
      }
    }

    为了使用 ArthurDeserialiser,我们需要向 GsonBuilder注册,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package com.javacreed.examples.gson.part2;
     
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.Reader;
     
    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
     
    public class Main {
     
      public static void main(final String[] args) throws IOException {
        // Configure GSON
        final GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(Book.class, new BookDeserializer());
        gsonBuilder.registerTypeAdapter(Author.class, new AuthorDeserializer());
        final Gson gson = gsonBuilder.create();
     
        // Read the JSON data
        try (Reader reader = new InputStreamReader(Main.class.getResourceAsStream("/part2/sample.json"), "UTF-8")) {
     
          // Parse JSON to Java
          final Book book = gson.fromJson(reader, Book.class);
          System.out.println(book);
        }
      }
    }

    没有必要改变 BookDeserialiser 类,因为作者的反序列化委托给了 context变量。这是另一个使用 context 反序列化其他对象或嵌套对象的优点。运行上面的代码将产生下面的输出。

    1
    2
    3
    4
    Java Puzzlers: Traps, Pitfalls, and Corner Cases [032133678X]
    Written by:
      >> [1] Joshua Bloch
      >> [2] Neal Gafter

    至此我们结束了嵌套对象的介绍。下一章节我们将看到如何引用JSON对象树中其他位置的JSON对象。

    对象引用

    考虑下面的JSON。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    {
      'authors': [
        {
          'id': 1,
          'name': 'Joshua Bloch'
        },
        {
          'id': 2,
          'name': 'Neal Gafter'
        }
      ],
      'books': [
        {
          'title': 'Java Puzzlers: Traps, Pitfalls, and Corner Cases',
          'isbn': '032133678X',
          'authors':[1, 2]
        },
        {
          'title': '<span class="wp_keywordlink"><a href="http://www.amazon.com/gp/product/B000WJOUPA/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B000WJOUPA&linkCode=as2&tag=job0ae-20" title="Effective Java" rel="nofollow" target="_blank" class="external">Effective Java</a></span> (2nd Edition)',
          'isbn': '0321356683',
          'authors':[1]
        }
      ]
    }

    上面的JSON对象由两个作者和两本书组成。书有一个id到作者的引用,本例中书的 authors 字段只包含作者的id。这是一个很常见的场景,通过这个方法将减少JSON对象的大小,因为重复对象通过他们的id进行引用。下图展示了JSON新对象的层级。

    JSON新对象层级

    这类似于关系型数据库(维基),其中book对象有一个到作者表的外键(维基)。新的JSON对象引入了需要解决的新挑战。当反序列化书籍对象时,我们需要保持作者对象,并从JSON对象层级的其他分支反序列化它们。书籍对象只有作者对象的id。作者对象的其他信息不在当前上下文中,需要从其他地方进行解析。

    这里有多种方式来解决这个问题,下面列出了一些。

    1. 一种方法是分两个阶段处理。首先将JSON对象解析成Java对象,这一步反序列化JSON中的书和作者对象。书籍类包含作者的id数组而不是作者的数组。接着第二阶段,我们关联对象,将作者对象关联到书籍对象。下图显示了反解析流程。

    两阶段处理过程

    这个方法需要很多类,但是提供了很大的灵活性并且更好地分离了关注点。我们需要创建一组代表JSON对象的简单Java类,接着创建另一组满足我们需求(模型)的类。这个例子里,我们有一个 Book和一个Author类,总共两个。使用这个方法,我们最终将有4个类,两个代表书籍类,另两个代表作者类。本例使用这个方法似乎是可行的,但是当有数十个类时,就会变得十分复杂。

    2. 另一种方法是提供包含所有作者的BookDeserialiser类,接着使用反序列化器从公用对象检索所有作者。这种方法消除了中间状态,因为JSON对象没有经过中间阶段就被反序列化成适当的Java对象。

    反序列化器共享对象

    尽管这个方法听上去有吸引力,但它要求 BookDeserialiser和 AuthorDeserialiser共享一个对象。此外,当当检索作者时, BookDeserialiser不得不引用这个共享对象来代替之前使用的JsonDeserializationContext类。这个方法需要修改几个地方反序列化器和main() 函数都需要修改。

    3. AuthorDeserialiser可以换成反序列化的作者,并在下次指定ID的请求时返回它们。这个方法十分有吸引力,因为它充分利用了 JsonDeserializationContext ,并且使得关系透明。不幸的是,它增加了复杂性,AuthorDeserialiser需要处理缓存。按照这个说法,这种方法需要最少的修改,只有 AuthorDeserialiser需要修改。

    AuthorDeserialiser反序列化器使用缓存对象

    如上如所示,只有 AuthorDeserialiser类访问缓存对象。系统里的其他部分不知道这一点。

    所有方法都是可行的,而且每种都有他们的优缺点。我们将使用第三种方法,因为它对工程的影响最小。

    观察

    理论上,与另两种方法相比,第一个方法提供了更好地分离了关注点。我们可以在新的 Data类中处理关联逻辑。但与第三种方法相比,这需要很多修改。这就是使用第三种方法的原因。始终考虑改变,并尽量减少所需的工作。

    上面所示的JSON对象包含两个数组。我们需要新的Java类来反射这个JSON对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.javacreed.examples.gson.part3;
     
    public class Data {
     
      private Author[] authors;
      private Book[] books;
     
      // Methods removed for brevity
    }

    字段顺序决定了两个集合反序列化的顺序。在我们的例子中没有问题,在后面我们可以看到,书籍集合可以在作者集合之前被反序列化。

    AuthorDeserialiser类需要做出修改,它会缓存反序列化出的作者对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    package com.javacreed.examples.gson.part3;
     
    import java.lang.reflect.Type;
    import java.util.HashMap;
    import java.util.Map;
     
    import com.google.gson.JsonDeserializationContext;
    import com.google.gson.JsonDeserializer;
    import com.google.gson.JsonElement;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParseException;
    import com.google.gson.JsonPrimitive;
     
    public class AuthorDeserializer implements JsonDeserializer<Author> {
     
      private final ThreadLocal<Map<Integer, Author>> cache = new ThreadLocal<Map<Integer, Author>>() {
        @Override
        protected Map<Integer, Author> initialValue() {
          return new HashMap<>();
        }
      };
     
      @Override
      public Author deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
          throws JsonParseException {
     
        // Only the ID is available
        if (json.isJsonPrimitive()) {
          final JsonPrimitive primitive = json.getAsJsonPrimitive();
          return getOrCreate(primitive.getAsInt());
        }
     
        // The whole object is available
        if (json.isJsonObject()) {
          final JsonObject jsonObject = json.getAsJsonObject();
     
          final Author author = getOrCreate(jsonObject.get("id").getAsInt());
          author.setName(jsonObject.get("name").getAsString());
          return author;
        }
     
        throw new JsonParseException("Unexpected JSON type: " + json.getClass().getSimpleName());
      }
     
      private Author getOrCreate(final int id) {
        Author author = cache.get().get(id);
        if (author == null) {
          author = new Author();
          author.setId(id);
          cache.get().put(id, author);
        }
        return author;
      }
    }

    我们在这个类中做了一些修改。让我们一个个地说明。

    1.作者保存在下面的对象中

    1
    2
    3
    4
    5
    6
    private final ThreadLocal<Map<Integer, Author>> cache = new ThreadLocal<Map<Integer, Author>>() {
      @Override
      protected Map<Integer, Author> initialValue() {
        return new HashMap<>();
      }
    };

    它使用 Map<String, Object> 提供缓存机制。map变量保存在ThreadLocal (Java文档)中,以隔离多线程之间的状态。这个类允许多线程使用相同的变量而不会影响到其他线程。

    观察

    请注意,尽管这个方法是线程安全的,但它并不满足特定应用领域的需求,因此不得不使用其他方法。更多缓存实例,请参考如何缓存结果来提高性能缓存使Spring更高效

    2.总是通过下面方法得到作者。

    1. 1
      2
      3
      4
      5
      6
      7
      8
      private Author getOrCreate(final int id) {
         Author author = cache.get().get(id);
         if (author == null) {
           author = new Author();
           cache.get().put(id, author);
         }
         return author;
       }

    这个方法首先从缓存中获取作者实例,如果没有找到给定id的作者,那么将创建一个并加入到缓存中。

    这个方法允许我们只用id就能创建作者,之后当它们可用时公布它们的内容。这就是为什么反序列化属性不影响输出。我们可以先反序列化书籍类,再反序列化作者类。在这个例子中,首先使用id创建作者,然后给他们增加名字。

    3.对 deserialize()函数进行修改来处理新的需求。因为修改了很多地方,我们将拆分这个方法,并逐个讲解。反序列化器可以接收JsonPrimitive 变量或者JsonObject变量。 当BookDeserialiser执行下面的代码时,传递给 AuthorDeserialiser 的 JsonElement变量将会是一个JsonPrimitive实例。

    1
    // This is executed within the BookDeserialiser Author[] authors = context.deserialize(jsonObject.get("authors"), Author[].class);

    下图显示了这个过程。

    委托反序列化给上下文

    上下文收到 BookDeserialiser 委托反序列化 Author数组的操作,并返回一个整型数组。对于每个整数,上下文作为一个 JsonPrimitive对象传递给 AuthorDeserialiser 的 deserialize()方法。

    另一方面,当作者被反序列化后,我们将收到一个包含作者和他或她详细信息的 JsonObject实例。因此,在我们转换给定的JsonElement对象前,需要校验他是否是正确的类型。

    1
    2
    3
    4
    5
    6
    // Only the ID is available
    if (json.isJsonPrimitive()) {
      final JsonPrimitive primitive = json.getAsJsonPrimitive();
      final Author author = getOrCreate(primitive.getAsInt());
      return author;
    }

    上例所示,只有id是有效的。 JsonElement 转换为JsonPrimitive接着又转换为 int.。

    JsonElement可以是JsonObject类型,如下所示。

    1
    2
    3
    4
    5
    6
    7
    8
    // The whole object is available
    if (json.isJsonObject()) {
      final JsonObject jsonObject = json.getAsJsonObject();
     
      final Author author = getOrCreate(jsonObject.get("id").getAsInt());
      author.setName(jsonObject.get("name").getAsString());
      return author;
    }

    这个例子中,在返回author前,向 getOrCreate()方法返回的 Author实例中增加name字段。

    最后,如果给定的JsonElement实例既不是 JsonPrimitive 也不是 JsonObject,将抛出一个异常说明不支持指定类型。

    1
    throw new JsonParseException("Unexpected JSON type: " + json.getClass().getSimpleName());

    以上代码块列出了所有需要的修改以适应和应对新的挑战。BookDeserialiser类和 main()方法不需要任何修改。执行main()将得到如下输出。

    1
    Output missing...

    这个例子总结了我们关于Gson反序列化器的文章。使用定制反序列化器不困难,可以使我们毫不费力的处理不同的JSON示例。需要注意的是,在Java业务对象不需要与解析的JSON对象对应。此外,我们可以使用新的JSON表示现有的Java对象。有些问题可能比其他问题解决起来更具挑战性。试着最大程度减少对现有代码的修改,你的设计将更具灵活性(尽可能不要修改)。

    原文链接: javacreed 翻译: ImportNew.com liken
    译文链接: http://www.importnew.com/16786.html

  • 相关阅读:
    解决Windows Server2008 R2中IE开网页时弹出阻止框
    为Java说句公道话
    垃圾回收(GC)的三种基本方式
    偏执却管用的10条Java编程技巧
    学习Javascript的8张思维导图【收藏】
    Java 常见异常及趣味解释
    Java程序员们最常犯的3个集合错误
    浅谈jsp、freemarker、velocity区别
    ThreadLocal使用
    javascript 之闭包
  • 原文地址:https://www.cnblogs.com/GarfieldEr007/p/6821675.html
Copyright © 2020-2023  润新知