探究一下Gson中的高级主题

本文介绍 Gson 库在 Java 中处理复杂 JSON 数据的高级技巧,包括对象嵌套序列化与反序列化、自定义序列化器和反序列化器、解决对象引用重复问题等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

经过Gson最简单入门Gson常用用法之后,再探讨一下Gson中的高级主题.这里主要讨论两个,对象的嵌套&&对象的引用.

对象嵌套

  • 定义要解析的POJO——Book&&Author
package com.example;

public class Author {

    private int id;
    private String name;
    //getter && setter
    //为了便于观察效果,这里重载一下toString
     @Override
    public String toString() {
        return "id: "+id+"\tname: "+name;
    }
package com.example;

import java.util.Arrays;

public class Book {

    private Author[] authors;
    private String title;
      //getter && setter
    //为了便于观察效果,这里重载一下toString
     @Override
    public String toString() {
        String infoMsg = "Book POJO msg as follow:\n";
        System.out.println(infoMsg);
        return "title:  " + title + "\t\t\tauthors: " + Arrays.toString(authors);
    }

使用Annotation
- 调用&&测试
这里Author&&Book中的成员变量和JSON中的key一致

package com.example;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.IOException;

public class Main {

    public static void main(final String[] args) throws IOException {
        // Configure GSON
        final GsonBuilder gsonBuilder = new GsonBuilder();
        //在生产环境中最好屏蔽,由于JSON格式化,可能产生大的JSON对象
        gsonBuilder.setPrettyPrinting();
        final Gson gson = gsonBuilder.create();

        final Book javaPuzzlers = new Book();
        javaPuzzlers.setTitle("English ABC");

        Author authorA=new Author();
        authorA.setId(1235);
        authorA.setName("gu");

        Author authorB=new Author();
        authorB .setId(1432);
        authorB.setName("hang");

        Author[] authors={authorA,authorB};

        javaPuzzlers.setAuthors(authors);

        // Format to JSON
        final String json = gson.toJson(javaPuzzlers);
        System.out.println(json);

    }
}

打印结果:

{
  "authors": [
    {
      "id": 1235,
      "name": "gu"
    },
    {
      "id": 1432,
      "name": "hang"
    }
  ],
  "title": "English ABC"
}

当然了,如果成员变量名称不一致,采用SerializedName Annotation也可。不熟悉的同学请看这里.

使用自定义Serializer&&Deserializer

  • 完成Book序列化方法
    这个与之前的大致相同,不同点就是对于内嵌对象的处理上,
package com.example;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Type;

public class BookSerializer implements JsonSerializer<Book> {

    @Override
    public JsonElement serialize(final Book book, final Type typeOfSrc, final JsonSerializationContext context) {
        final JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("title", book.getTitle());
        //*******************************************************
        JsonElement authorElement = context.serialize(book.getAuthors(), Author[].class);
        //*******************************************************
        jsonObject.add("authors", authorElement);

        return jsonObject;
    }
}

仅仅调用这一句,就可以完成内嵌对象的解析.
这个context就去调用内嵌对象Author的serialize(),去完成对象的序列化,自然内嵌对象的序列化就和之前讲的Gson的常用操作没有任何区别.

注意

  public JsonElement serialize(Object src, Type typeOfSrc);

java中Book数组代表的Type就为Book[].class;
如果是一个单独的Book对象,自然就写成Book.class;
如果是一个List<Book>

import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
Type type=new TypeToken<List<Book>>(){}.getType();
  • 调用&&测试
package com.example;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.IOException;

public class Main {

    public static void main(final String[] args) throws IOException {
        final GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setPrettyPrinting();
        //注册自定义序列化接口
        gsonBuilder.registerTypeAdapter(Book.class,new BookSerializer());
        //如果Author也使用了自定义Serializer,也需要在这里进行注册
        final Gson gson = gsonBuilder.create();

        final Book javaPuzzlers = new Book();
        javaPuzzlers.setTitle("English ABC");

        Author authorA=new Author();
        authorA.setId(1235);
        authorA.setName("gu");

        Author authorB=new Author();
        authorB .setId(1432);
        authorB.setName("hang");

        Author[] authors={authorA,authorB};

        javaPuzzlers.setAuthors(authors);

        // Format to JSON
        final String json = gson.toJson(javaPuzzlers);
        System.out.println(json);

    }
}

测试结果:

{
  "authors": [
    {
      "id": 1235,
      "name": "gu"
    },
    {
      "id": 1432,
      "name": "hang"
    }
  ],
  "title": "English ABC"
}

反序列话非常类似,在这里罗列一下代码.

package com.example;

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 java.lang.reflect.Type;

public class BookDeserializer implements JsonDeserializer<Book> {

    @Override
    public Book deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        JsonObject jsonObject = jsonElement.getAsJsonObject();

        JsonElement titleElement = jsonObject.get("title");
        String title = "";
        //注意:如果要反序列化的JSON中没有title这个key,不进行null判断,会崩溃
        if (titleElement != null) {
            title = titleElement.getAsString();
        }
        //*******************************反序列化内嵌对象***********
        Author[] authors = jsonDeserializationContext.deserialize(jsonObject.get("authors"), Author[].class);
        //*********************************反序列化内嵌对象***********

        Book book = new Book();
        book.setTitle(title);
        book.setAuthors(authors);
        return book;
    }
}
  • 调用&&测试
package com.example;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.IOException;

public class Main {
public static final String TEST="{\n" +
        "  \"authors\": [\n" +
        "    {\n" +
        "      \"id\": 1235,\n" +
        "      \"name\": \"gu\"\n" +
        "    },\n" +
        "    {\n" +
        "      \"id\": 1432,\n" +
        "      \"name\": \"hang\"\n" +
        "    }\n" +
        "  ],\n" +
        "  \"title\": \"English ABC\"\n" +
        "}";
    public static void main(final String[] args) throws IOException {
        final GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setPrettyPrinting();
        //注册自定义反序列化接口
        gsonBuilder.registerTypeAdapter(Book.class,new BookSerializer());
        //如果Author也使用了自定义Serializer,也需要在这里进行注册
        final Gson gson = gsonBuilder.create();
        Book book=gson.fromJson(TEST,Book.class);
        System.out.println(book);

    }
}

测试结果:

Book POJO msg as follow:

title:  English ABC         authors: [id: 1235  name: gu, id: 1432  name: hang]

对象引用

{
  'authors': [
    {
      'id': 1,
      'name': 'Joshua Bloch'
    },
    {
      'id': 2,
      'name': 'Neal Gafter'
    }
  ],
  'books': [
    {
      'title': 'Java Puzzlers: Traps, Pitfalls, and Corner Cases',
      'authors':[1, 2]
    },
    {
      'title': 'Effective Java (2nd Edition)',
      'authors':[1]
    }
  ]
}

客户端接收到这样的数据,如何填充Book中的Author的name字段呢?下面就探究其中的一种实现.
1.将上述Gson格式字段保存到项目根路径下的Input.json文件中.
2.定义新的类型Data用于接收全部数据

package com.example;

import java.util.Arrays;

public class Data {

    private Author[] authors;
    private Book[] books;
    //getter && setter
    //重载toString
     @Override
    public String toString() {
        return "authors: "+ Arrays.toString(authors)+"\tbooks:"+Arrays.toString(books);
    }

3.定义AuthorDeserializer完成反序列化

package com.example;

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;
    }
}

这里ThreadLocal<Map<Integer, Author>> cache持有了所有Author对象的引用,并且将相同id的Author指向同一个Author,这与指针挺像,修改id对应的Author将会修改所有持有该id的Author.

package com.example;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

public class Main {

    public static void main(final String[] args) throws IOException {
        final GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setPrettyPrinting();

        //注册自定义反序列化接口
        gsonBuilder.registerTypeAdapter(Author.class, new AuthorDeserializer());
        try (Reader reader = new InputStreamReader(new FileInputStream("Input.json"))) {

            final Gson gson = gsonBuilder.create();
            Data data = gson.fromJson(reader, Data.class);
            System.out.println(data);
        }


    }
}

测试结果:

authors: [id: 1 name: Joshua Bloch, id: 2   name: Neal Gafter]  books:[title:  Java Puzzlers: Traps, Pitfalls, and Corner Cases         authors: [id: 1 name: Joshua Bloch, id: 2   name: Neal Gafter], title:  Effective Java (2nd Edition)            authors: [id: 1 name: Joshua Bloch]]

参考地址:http://www.javacreed.com/gson-deserialiser-example/

引入

package com.example;

import java.io.IOException;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Example1 {

    public static void main(final String[] args) throws IOException {
        final GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setPrettyPrinting();
        final Gson gson = gsonBuilder.create();

        final Author joshuaBloch = new Author();
        joshuaBloch.setId(1);
        joshuaBloch.setName("Joshua Bloch");

        final Author nealGafter = new Author();
        nealGafter.setId(2);
        nealGafter.setName("Neal Gafter");

        final Book javaPuzzlers = new Book();
        javaPuzzlers.setTitle("Java Puzzlers: Traps, Pitfalls, and Corner Cases");
        javaPuzzlers.setAuthors(new Author[] { joshuaBloch, nealGafter });

        final Book effectiveJava = new Book();
        effectiveJava.setTitle("Effective Java (2nd Edition)");
        effectiveJava.setAuthors(new Author[] { joshuaBloch });

        final Book[] books = new Book[] { javaPuzzlers, effectiveJava };

        final String json = gson.toJson(books);
        System.out.println(json);
    }
}

打印结果:

[
  {
    "authors": [
      {
        "id": 1,
        "name": "Joshua Bloch"
      },
      {
        "id": 2,
        "name": "Neal Gafter"
      }
    ],
    "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases"
  },
  {
    "authors": [
      {
        "id": 1,
        "name": "Joshua Bloch"
      }
    ],
    "title": "Effective Java (2nd Edition)"
  }
]

很显然这里id=1的author出现了两次,可以缩减为1?

  • 在Book中添加一个返回id数组的方法
  public int[] getAuthorsIds() {
    final int[] ids = new int[authors.length];
    for (int i = 0; i < ids.length; i++) {
      ids[i] = authors[i].getId();
    }
    return ids;
  }
  • 修改BookSerializer方法,Author字段仅仅填充ids
package com.example;

import java.lang.reflect.Type;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

public class BookSerializer implements JsonSerializer<Book> {

    @Override
    public JsonElement serialize(final Book book, final Type typeOfSrc, final JsonSerializationContext context) {
        final JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("title", book.getTitle());
        final JsonElement jsonAuthors = context.serialize(book.getAuthorsIds());
//   old:    JsonElement authorElement = context.serialize(book.getAuthors(), Author[].class);
        jsonObject.add("authors", jsonAuthors);

        return jsonObject;
    }
}
  • 调用&&测试
package com.example;

import java.io.IOException;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Example1 {

    public static void main(final String[] args) throws IOException {
        Book[] books = null;
        //制造数据
        {
            final Author joshuaBloch = new Author();
            joshuaBloch.setId(1);
            joshuaBloch.setName("Joshua Bloch");

            final Author nealGafter = new Author();
            nealGafter.setId(2);
            nealGafter.setName("Neal Gafter");

            final Book javaPuzzlers = new Book();
            javaPuzzlers.setTitle("Java Puzzlers: Traps, Pitfalls, and Corner Cases");
            javaPuzzlers.setAuthors(new Author[]{joshuaBloch, nealGafter});

            final Book effectiveJava = new Book();
            effectiveJava.setTitle("Effective Java (2nd Edition)");
            effectiveJava.setAuthors(new Author[]{joshuaBloch});

            books = new Book[]{javaPuzzlers, effectiveJava};
        }
        //精简冗余数据
        {
            GsonBuilder gsonBuilder = new GsonBuilder();
            gsonBuilder.registerTypeAdapter(Book.class, new BookSerializer());
            Gson gson = gsonBuilder.create();
            String bookArrString = gson.toJson(books);
            System.out.println(bookArrString);
        }


    }
}

打印结果:

[
  {
    "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
    "authors": [
      1,
      2
    ]
  },
  {
    "title": "Effective Java (2nd Edition)",
    "authors": [
      1
    ]
  }
]

参考地址:http://www.javacreed.com/gson-serialiser-example/

包含以下java源文件: com.google.gson.DefaultDateTypeAdapter.class com.google.gson.ExclusionStrategy.class com.google.gson.FieldAttributes.class com.google.gson.FieldNamingPolicy.class com.google.gson.FieldNamingStrategy.class com.google.gson.Gson.class com.google.gson.GsonBuilder.class com.google.gson.InstanceCreator.class com.google.gson.JsonArray.class com.google.gson.JsonDeserializationContext.class com.google.gson.JsonDeserializer.class com.google.gson.JsonElement.class com.google.gson.JsonIOException.class com.google.gson.JsonNull.class com.google.gson.JsonObject.class com.google.gson.JsonParseException.class com.google.gson.JsonParser.class com.google.gson.JsonPrimitive.class com.google.gson.JsonSerializationContext.class com.google.gson.JsonSerializer.class com.google.gson.JsonStreamParser.class com.google.gson.JsonSyntaxException.class com.google.gson.LongSerializationPolicy.class com.google.gson.TreeTypeAdapter.class com.google.gson.TypeAdapter.class com.google.gson.TypeAdapterFactory.class com.google.gson.annotations.Expose.class com.google.gson.annotations.SerializedName.class com.google.gson.annotations.Since.class com.google.gson.annotations.Until.class com.google.gson.internal.ConstructorConstructor.class com.google.gson.internal.Excluder.class com.google.gson.internal.JsonReaderInternalAccess.class com.google.gson.internal.LazilyParsedNumber.class com.google.gson.internal.LinkedTreeMap.class com.google.gson.internal.ObjectConstructor.class com.google.gson.internal.Primitives.class com.google.gson.internal.Streams.class com.google.gson.internal.UnsafeAllocator.class com.google.gson.internal.bind.ArrayTypeAdapter.class com.google.gson.internal.bind.CollectionTypeAdapterFactory.class com.google.gson.internal.bind.DateTypeAdapter.class com.google.gson.internal.bind.JsonTreeReader.class com.google.gson.internal.bind.JsonTreeWriter.class com.google.gson.internal.bind.MapTypeAdapterFactory.class com.google.gson.internal.bind.ObjectTypeAdapter.class com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.class com.google.gson.internal.bind.SqlDateTypeAdapter.class com.google.gson.internal.bind.TimeTypeAdapter.class com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.class com.google.gson.internal.bind.TypeAdapters.class com.google.gson.reflect.TypeToken.class com.google.gson.stream.JsonReader.class com.google.gson.stream.JsonScope.class com.google.gson.stream.JsonToken.class com.google.gson.stream.JsonWriter.class com.google.gson.stream.MalformedJsonException.class
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值