Json 序列化一 —— 基础篇

1、简介

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它的作用是数据标记,存储,传输,具有读写速度快、解析简单、轻量级、独立于语言和平台、具有自我描述性等特点。

在这里插入图片描述

像 gson、fastjson、jackson 这些都是 JSON 的解析器。而 gson 又称为 Google Gson,是 Google 发布的开源 Java 库,主要用于把 Java 对象序列化为 JSON 字符串,或把 JSON 字符串反序列化为 Java 对象。

2、JSON 语法

JSON 的语法构建于 key-value 键值对和值的有效列表(数组)之上。这两种都是常见的数据结构,大部分计算机语言都以某种形式支持它们,这使得 JSON 数据格式在同样基于这些结构的编程语言之间交换成为可能。

JSON 具有对象(Object)、数组(Array)、值(Value)、字符串(String)、数值(Number)等形式。

2.1 对象 Object

无序的键值对集合,以"{“开始,以”}“结束,在 key 后面跟一个”:“,键值对之间用”,"隔开。

    {
      "name": "英语",
      "score": 78.3
    }

2.2 值 Value

可以是双引号括起来的字符串、数值、true、false、null、对象或数组,可以嵌套。

在这里插入图片描述

{
    "url":"https://qqe2.com",
    "name": "欢迎使用JSON在线解析编辑器",
    "array": { 
        "JSON校验": "http://jsonlint.qqe2.com/", 
        "Cron生成": "http://cron.qqe2.com/", 
        "JS加密解密": "http://edit.qqe2.com/" },
    "boolean": true,
    "null": null, 
    "number": 123, 
    "object": { 
        "a": "b", 
        "c": "d", 
        "e": "f"
    }
}

2.3 数组 Array

有序的值的集合。以"[“开始,以”]“结束。值之间使用”,"分隔。

"courses": [ 
    // 一个对象{}作为一个 value,中间用逗号隔开
    {
        "name": "英语", 
        "score": 78.3 
    },
    {
        "name": "数学", 
        "score": 88.3 
    }
]

2.4 字符串 String

是由双引号包围的任意数量 Unicode 字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。

{ 
    "name": "Zero", 
}

2.5 数值 Number

与 C 或者 Java 的数值非常相似。除去未曾使用的八进制与十六进制格式和一些编码细节。

{ 
    "age": 28, 
}

3、Gson 基础

3.1 Gson 的基本使用

public class GsonTest {

    static class GsonBean {
        private int i;
        private String string;

        public GsonBean(int i, String string) {
            this.i = i;
            this.string = string;
        }

        @Override
        public String toString() {
            return "GsonBean{" +
                    "i=" + i +
                    ", string='" + string + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
        Gson gson = new Gson();
        // 基本类型的序列化
        System.out.println(gson.toJson(1)); // 1
        System.out.println(gson.toJson("test")); // "test"
        System.out.println(gson.toJson(new int[]{1, 2, 3})); // [1,2,3]
        // 反序列化
        Integer i = gson.fromJson("1", int.class);
        System.out.println("i=" + i); // i=1
        // 引用类型的序列化与反序列化
        GsonBean gsonBean = new GsonBean(2, "test");
        String json = gson.toJson(gsonBean);
        System.out.println("json:" + json); // json:{"i":2,"string":"test"}

        GsonBean gsonBean1 = gson.fromJson(json, GsonBean.class);
        System.out.println("gsonBean1" + gsonBean1); // gsonBean1GsonBean{i=2, string='test'}
    }
    
    /**
     * 将 gsonBean 序列化成 JSON 格式并写入 filePath 指定的文件中
     */
    private static void writeJsonToFile(GsonBean gsonBean, String filePath) {
        try (OutputStream os = new FileOutputStream(filePath);
             JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) {

            Gson gson = new Gson();
            gson.toJson(gsonBean, new TypeToken<GsonBean>() {
            }.getType(), jsonWriter);
            jsonWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static GsonBean readJsonObjectFromFile(String filePath) {
        try (JsonReader jsonReader = new JsonReader(new InputStreamReader(new FileInputStream(filePath)))) {
            Gson gson = new Gson();
            return gson.fromJson(jsonReader, new TypeToken<GsonBean>() {
            }.getType());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

此外,给定一个 JSON 格式,需要熟练的将其转换成 JavaBean,如:

// 最外层是一个对象,里面有key、simpleArray、arrays、innerclass四个对象
{
    // 字符串对象
    "key": "value",
    // [] 表示数组,数组内对象只有value没有key就是纯数组
    "simpleArray": [1,2,3],
    // 两组[]说明是数组嵌套,内层[]内放了一个对象,该对象对应Java的内部类
    "arrays": [
        [{
        "arrInnerClsKey": "arrInnerClsValue",
        "arrInnerClsKeyNub": 1
        }]
    ],
    // 对象嵌套,对应Java的内部类,其对象名为父对象的key,即innerclass
    "innerclass": {
    "name": "zero",
    "age": 25,
    "sex": "男"
    }
}

转换成 JavaBean 是:

public class GsonBean {

    private String key;
    private List<Integer> simpleArray;
    private List<List<ArraysBean>> arrays;
    private InnerClassBean innerclass;
    
    // getters and setters...

    public static class ArraysBean {

        private String arrInnerClsKey;
        private int arrInnerClsKeyNub;

        // getters and setters...
    }

    public static class InnerClassBean {

        private String name;
        private int age;
        private String sex;

        // getters and setters...
    }
}

3.2 Gson 中的注解

Gson 中有一些注解,会给序列化/反序列化工作提供很大的便利。

SerializedName 注解

@SerializedName 表示序列化时,会用给定的名字作为 key:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface SerializedName {

  /**
   * @return 在序列化/反序列化时,返回该字段想要得到的名字
   */
  String value();
  /**
   * @return 反序列化时,返回该字段可选的名字
   */
  String[] alternate() default {};
}

示例代码:

    public class MyClass {
        @SerializedName("name")
        String a;
        @SerializedName(value = "name1", alternate = {"name2", "name3"})
        String b;
        String c;

        public MyClass(String a, String b, String c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }

    private void test() {
        MyClass target = new MyClass("v1", "v2", "v3");
        Gson gson = new Gson();
        String json = gson.toJson(target);
        System.out.println(json); // {"name":"v1","name1":"v2","c":"v3"}

        MyClass target1 = gson.fromJson("{'name1':'v1'}", MyClass.class);
        assertEquals("v1", target1.b);
        target1 = gson.fromJson("{'name2':'v2'}", MyClass.class);
        assertEquals("v2", target1.b);
        target1 = gson.fromJson("{'name3':'v3'}", MyClass.class);
        assertEquals("v3", target1.b);
    }

    private void assertEquals(String value, String keyValue) {
        assert value.equals(keyValue);
    }

MyClass 中,字段 a 在序列化到 Json 中之后的名字为 name,字段 b 序列化后的名字为 name1(如果指定了多个值,会用第一个),而字段 c 则采用默认策略,序列化的名字就为变量名 c。

反序列化时,Json 中的 name1、name2、name3 都是字段 b 所指定的名字,所以它们都会被反序列化到字段 b 上。

@SerializedName 主要用途就是解决后端返回的 Json 字段名字不便建立 JavaBean 字段的情况,比如说后端给的 Json 是这样的:

{
	"Type": 6,
	"Result": {
		"State": "1",
		"Msg": "成功"
	},
	"Data": {
		"1": 1000000001,
		"2": 2800,
		"3": "UPDATE",
		"4": 201901021130,
		"5": 2854,
		"6": 2,
		"7": 2.58,
	}
}

Data 中的1、2、3、4、5、6、7根本无法作为 Java 字段声明,这时就可以用 @SerializedName 注解:

public class TestPojo {
    @SerializedName("1")
    private int totalAccount;
    @SerializedName("2")
    private int numIns;
    @SerializedName("3")
    private String type;
    @SerializedName("4")
    private long time;
}

Expose 注解

还有一个 @Expose 注解用来标明该字段是否参与序列化/反序列化:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Expose {
  
  /**
   * If {@code true}, the field marked with this annotation is written out in the JSON while
   * serializing. If {@code false}, the field marked with this annotation is skipped from the
   * serialized output. Defaults to {@code true}.
   * @since 1.4
   */
  public boolean serialize() default true;

  /**
   * If {@code true}, the field marked with this annotation is deserialized from the JSON.
   * If {@code false}, the field marked with this annotation is skipped during deserialization. 
   * Defaults to {@code true}.
   * @since 1.4
   */
  public boolean deserialize() default true;
}

Since 和 Until 注解

此外还有用于版本控制的注解 @Since 和 @Until:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since {
  /**
   * the value indicating a version number since this member
   * or type has been present.
   */
  double value();
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until {

  /**
   * the value indicating a version number until this member
   * or type should be ignored.
   */
  double value();
}

@Since 表示当被标记字段的当前版本大于等于 value() 指定的值时才参与序列化/反序列化,否则忽略;@Until 表示当前被标记的作用域的当前版本小于(注意没有等于) value() 指定的值时才参与序列化/反序列化,否则忽略。

代码示例:

public class User {
    private String name;
    @Since(1.0)
    @Until(1.3)
    private String emailAddress;
    @Since(1.0)
    @Until(1.2)
    private String password;
    
    // 构造方法和 toString() 省略
}

name 字段会参加所有版本的序列化/反序列化,emailAddress 会参加 [1.0,1.3) 版本之间的序列化/反序列化,password 会参加 [1.0,1.2) 版本之间的序列化/反序列化。注意需要使用 GsonBuilder 指定当前版本后创建 Gson,不能使用 Gson 的空构造方法,否则 User 中使用 @Since、@Until 指定的版本将无效:

    // 使用空参构造方法创建 Gson
	private void testUser() {
        Gson gson = new Gson();
        User user = new User("Test", "xyz@test.com", "123456");

        String json = gson.toJson(user);
        System.out.println(json); // {"name":"Test","emailAddress":"xyz@test.com","password":"123456"}

        User user1 = gson.fromJson(json, User.class);
        System.out.println(user1); // User{name='Test', emailAddress='xyz@test.com', password='123456'}
    }

	// 使用 GsonBuilder 创建 Gson 并设置其版本号
	private void testUserWithVersion() {
        Gson gson1 = new GsonBuilder().setVersion(1.1).create();
        User user = new User("Test", "xyz@test.com", "123456");

        String json1 = gson1.toJson(user);
        System.out.println(json1); // {"name":"Test","emailAddress":"xyz@test.com","password":"123456"}

        User user1 = gson1.fromJson(json1, User.class);
        System.out.println(user1); // User{name='Test', emailAddress='xyz@test.com', password='123456'}


        Gson gson2 = new GsonBuilder().setVersion(1.2).create();

        String json2 = gson2.toJson(user);
        System.out.println(json2); // {"name":"Test","emailAddress":"xyz@test.com"}

        User user2 = gson2.fromJson(json2, User.class);
        System.out.println(user2); // User{name='Test', emailAddress='xyz@test.com', password='null'}
    }

当指定 gson2 的版本号为 1.2 时,password 字段没有参与序列化/反序列化,说明 @Until 指定的边界不在参与序列化/反序列化的版本之内。

JsonAdapter 注解

Gson 中的解析使用了反射技术,由于反射会使性能受到影响,因此 Gson 提供了 @JsonAdapter 注解用来指定一个具体的 JavaBean 的解析类来代替默认的反射。源码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface JsonAdapter {

  /** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}, or one or both of {@link JsonDeserializer} or {@link JsonSerializer}. */
  Class<?> value();

  /** false, to be able to handle {@code null} values within the adapter, default value is true. */
  boolean nullSafe() default true;

}

@JsonAdapter 既可以作用于类,也可以作用于字段,先看如何在类上使用。JavaBean 类上加注解 @JsonAdapter,值为代替默认的反射方式解析 Json 的类对象:

@JsonAdapter(PersonJsonAdapter.class)
public class Person {

    public final String firstName, lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    // toString()...
}

PersonJsonAdapter 类需要继承 TypeAdapter 并实现 write() 和 read():

public class PersonJsonAdapter extends TypeAdapter<Person> {

    // implement write: combine firstName and lastName into name
    @Override
    public void write(JsonWriter out, Person value) throws IOException {
        out.beginObject();
        out.name("name");
        out.value(value.firstName + " " + value.lastName);
        out.endObject();
    }

    // implement read: split name into firstName and lastName
    @Override
    public Person read(JsonReader in) throws IOException {
        in.beginObject();
        in.nextName();
        String[] nameParts = in.nextString().split(" ");
        in.endObject();
        return new Person(nameParts[0], nameParts[1]);
    }
}

write() 其实就是执行序列化过程,把 Person 类写成 Json 字符串,read() 就是反序列化把 Json 字符串转换成 Person 对象。

测试代码:

    private void testPerson() {
        Person person = new Person("first", "last");
        Gson gson = new Gson();

        String json = gson.toJson(person);
        System.out.println(json); // {"name":"first last"}
        System.out.println(gson.fromJson(json, Person.class)); // Person{firstName='first', lastName='last'}
    }

该注解作用于字段:

private static final class Gadget {
    @JsonAdapter(UserJsonAdapter.class)
    final User user;
    Gadget(User user) {
      this.user = user;
    }
}

可以在一个字段、该字段的类型以及 GsonBuilder 中(通过 registerTypeAdapter())指定不同的类型适配器(TypeAdapter),三者的优先级由高到低是:作用于字段的注解 -> 在 GsonBuilder 上注册的适配器 -> 作用于类上的注解。

这个注解引用的类必须是 TypeAdapter 或者 TypeAdapterFactory,或者是至少实现了 JsonSerializer 或 JsonDeserializer 接口之一。使用 TypeAdapterFactory 可以使该注解委派给一个封闭的 Gson 实例。

3.3 其它实用方法

主要是 GsonBuilder 中提供的常用方法:

  • setPrettyPrinting():输出 Json 的时候会按照 Json 的格式输出,默认是在一行中输出。
  • registerTypeAdapter():设置自定义的解析器(需要继承 TypeAdapter),是 @JsonAdapter 的等价替换方式。

3.4 对字段空值的处理

实际开发时可能会遇到后端返回的 Json 数据某个字段的值是一个空值,类似于:

{
    "name":"java",
    "authors":""
}

JavaBean 如下:

public class GsonError {

    private String name;
    private List<AuthorsBean> authors;

    public static class AuthorsBean {
        private String id;
        private String name;
    }
}

使用默认的 Gson 进行反序列化:

    public static void test2() {
        String json = "{\n" +
                "    \"name\": \"java\",\n" +
                "    \"authors\": \"\"\n" +
                "}";
        Gson gson = new Gson();
        GsonError gsonError = gson.fromJson(json, GsonError.class);

        System.out.println(gsonError);
    }

会抛出异常:

Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 3 column 17 path $.authors

异常原因是 authors 字段的值应该是一个数组,但是 JSON 数据给出的却是一个空的字符串,默认的 Gson 解析器无法处理这种类型不匹配的情况,因此抛出了异常。既然默认的解析器无法处理这种情况,那我们可以自定义反序列化过程对此做专门的处理,有两种方式:

1. 自定义 JsonDeserializer:实现 JsonDeserializer 接口,在处理反序列化的方法 deserialize() 内对可能出现问题的 key 进行特殊处理。
2. 自定义 TypeAdapter:继承 TypeAdapter,重写反序列化方法 read() 时对可能出现问题的 key 进行特殊处理。如果不想在 read()、write() 中手动处理空值,可以调用 TypeAdapter 的 nullSafe() 保证空值安全。

两种方式都需要通过 GsonBuilder 的 registerTypeAdapter() 注册才可生效。

自定义 JsonDeserializer

先来看第一种方式:

    public static class GsonErrorDeserializer implements JsonDeserializer<GsonError> {

        @Override
        public GsonError deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            JsonObject jsonObject = json.getAsJsonObject();
            GsonError gsonError = new GsonError();

            // 先处理 name
            String name = jsonObject.get("name").getAsString();
            gsonError.setName(name);

            // 再处理 author
            JsonElement authorElement = jsonObject.get("authors");
            if (!authorElement.isJsonArray()) {
                // 不是 Json 数组,视为无效值
                gsonError.setAuthors(null);
            } else {
                AuthorsBean[] authors = context.deserialize(authorElement, AuthorsBean[].class);
                gsonError.setAuthors(Arrays.asList(authors));
            }

            return gsonError;
        }
    }

    public static class AuthorDeserializer implements JsonDeserializer<GsonError.AuthorsBean> {

        @Override
        public AuthorsBean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            JsonObject jsonObject = json.getAsJsonObject();
            String id = jsonObject.get("id").getAsString();
            String name = jsonObject.get("name").getAsString();
            return new GsonError.AuthorsBean(id, name);
        }
    }

	private static void test2() {
        String json = "{\n" +
                "    \"name\": \"java\",\n" +
                "    \"authors\": \"\"\n" +
                "}";

        Gson gson = new GsonBuilder()
            	.registerTypeAdapter(GsonError.class, new GsonErrorDeserializer()) // 注册 GsonErrorDeserializer
                .registerTypeAdapter(GsonError.AuthorsBean.class, new AuthorDeserializer()) // 注册 AuthorDeserializer
                .create();
        GsonError gsonError = gson.fromJson(json, GsonError.class);
        System.out.println(gsonError); // GsonError{name='java', authors=null}
    }

这里是把两个 JsonDeserializer 的实现类作为 GsonError 的静态内部类了。通过 jsonObject.get(xxx) 得到的是 JsonElement 对象,可以用它的 isJsonArray() 判断当前的 Json 元素是否为一个数组。

自定义 TypeAdapter

再看第二种方式:

    public static void test3() {
        String json = "{\n" +
                "    \"name\": \"java\",\n" +
                "    \"authors\": \"\"\n" +
                "}";
        Gson gson = new GsonBuilder().registerTypeAdapter(GsonError.class,
                new TypeAdapter<GsonError>() {
                    @Override
                    public void write(JsonWriter out, GsonError value) throws IOException {
                        if (value == null) {
                            out.nullValue();
                            return;
                        }
                        // 写 GsonError 对象
                        out.beginObject(); // {
                        out.name("name").value(value.getName());

                        // 写数组
                        out.name("authors");
                        List<GsonError.AuthorsBean> authors = value.getAuthors();
                        if (authors == null) {
                            // 如果数组为空,这里有多种写法
                            out.value("null");
                            //out.nullValue();
                            //out.value("");
                        } else {
                            // 数组不为空,开始写数组
                            out.beginArray(); // [
                            for (int i = 0; i < authors.size(); i++) {
                                out.beginObject(); // {
                                out.name("id");
                                out.value(authors.get(i).getId());
                                out.name("name");
                                out.value(authors.get(i).getName());
                                out.endObject(); // }
                            }
                            out.endArray(); // ]
                        }
                        out.endObject(); // }
                    }

                    @Override
                    public GsonError read(JsonReader in) throws IOException {
                        // 先进行非空判断
                        if (in.peek() == JsonToken.NULL) {
                            // 如果做 doPeek() 操作拿出的 JsonToken 确实为 null,
                            // 就消费掉这个 JsonToken 并断言它是 null。
                            in.nextNull();
                            return null;
                        }

                        GsonError gsonError = new GsonError();
                        in.beginObject();
                        List<GsonError.AuthorsBean> authors = new ArrayList<>();
                        while (in.hasNext()) {
                            // 取下一个 key,并对 key 的值进行判断
                            switch (in.nextName()) {
                                // 如果 key 的值为 name,那么就把对应的 value 取出设置给 GsonError 的 name 字段。
                                case "name":
                                    gsonError.setName(in.nextString());
                                    break;
                                case "authors":
                                    // 先对 authors 的值进行判断,如果不是 [ 开头,说明是一个无效值。
                                    if (in.peek() != JsonToken.BEGIN_ARRAY) {
                                        authors = null;
                                        in.skipValue(); // 跳过这个 value
                                        continue;
                                    }

                                    // 如果 authors 的值是有效的数组值,开始解析
                                    in.beginArray();
                                    while (in.hasNext()) {
                                        String id = in.nextString();
                                        String name = in.nextString();
                                        authors.add(new GsonError.AuthorsBean(id, name));
                                    }
                                    in.endArray();
                                    break;
                            }
                        }
                        in.endObject();
                        gsonError.setAuthors(authors);
                        return gsonError;
                    }
                }).setPrettyPrinting().create();

        GsonError gsonError = gson.fromJson(json, GsonError.class);
        System.out.println(gsonError); // GsonError{name='java', authors=null}
    }

操作过程已经在注释中详细标出,其实就是在 read() 读取到 authors 的值时,对值做一个判断,如果是一个有效的数组值,那么它肯定是以 [ 开始的,如果不是,而是诸如 “”、“null”、null 等形式,就直接跳过这个 value 不处理。

另外在 write() 写值的时候,有个细节,就是如果 List<GsonError.AuthorsBean> authors 为空的时候值该怎么写的问题。示例代码中给出了三种写法,得到的序列化结果分别是:

out.value(""):
{
  "name": "java",
  "authors": ""
}

out.nullValue():
{
  "name": "java"
}

out.value("null"):
{
  "name": "java",
  "authors": "null"
}

可以看到当使用 out.nullValue() 时,生成的 Json 中没有对应的 key-value,即 “authors” 及其 value。

TypeAdapter 的 nullSafe()

倘若你在自定义 TypeAdapter 时不想对繁杂的空值进行处理,那么可以使用 TypeAdapter 的包装方法 nullSafe():

  /**
   * 此包装方法可使类型适配器具有空容忍能力。通常,需要类型适配器来处理写和读方法中的空值。下面
   * 是通常的做法:
   * <pre>   {@code
   *
   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
   *   new TypeAdapter<Foo>() {
   *     public Foo read(JsonReader in) throws IOException {
   *       if (in.peek() == JsonToken.NULL) {
   *         in.nextNull();
   *         return null;
   *       }
   *       // read a Foo from in and return it
   *     }
   *     public void write(JsonWriter out, Foo src) throws IOException {
   *       if (src == null) {
   *         out.nullValue();
   *         return;
   *       }
   *       // write src as JSON to out
   *     }
   *   }).create();
   * }</pre>
   * 使用此方法包装你的类型适配器,可以避免这种对空值的样板处理。以下代码展示了如何
   * 重写上面的例子。
   * <pre>   {@code
   *
   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
   *   new TypeAdapter<Foo>() {
   *     public Foo read(JsonReader in) throws IOException {
   *       // read a Foo from in and return it
   *     }
   *     public void write(JsonWriter out, Foo src) throws IOException {
   *       // write src as JSON to out
   *     }
   *   }.nullSafe()).create();
   * }</pre>
   * 注意,使用了 nullSafe 之后,就不需要在我们的类型适配器中检查空值了。
   */
  public final TypeAdapter<T> nullSafe() {
    return new TypeAdapter<T>() {
      @Override public void write(JsonWriter out, T value) throws IOException {
        // 如果是空值就不记录这条key-value
        if (value == null) {
          out.nullValue();
        } else {
          // 否则就用当前的 TypeAdapter 正常序列化
          TypeAdapter.this.write(out, value);
        }
      }
      @Override public T read(JsonReader reader) throws IOException {
        // 如果读到一个空值,就消费掉它  
        if (reader.peek() == JsonToken.NULL) {
          reader.nextNull();
          return null;
        }
        // 否则就用当前的 TypeAdapter 正常反序列化
        return TypeAdapter.this.read(reader);
      }
    };
  }

参考资料:
@SerializedName注解

### JSON 基础知识教程 #### 什么是 JSONJSON(JavaScript Object Notation)是种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成[^3]。尽管其名字中有“JavaScript”,但它实际上是个独立于语言的格式,被广泛应用于多种编程语言中。 #### JSON 的特点 - **跨平台性**:JSON 不依赖于任何特定的语言或平台,几乎所有主流编程语言(如 C、Python、C++、Java、PHP 和 Go 等)都支持 JSON 数据处理[^2]。 - **简洁性和可读性**:相比 XML,JSON 更加紧凑且易于理解,适合快速开发需求。 - **自描述性强**:通过简单的键值对形式表达复杂的数据结构,便于开发者直观了解数据含义。 --- #### JSON 的基本语法 ##### 键值对 JSON 中的核心单位是由键(key)和对应的值(value)组成的键值对。键必须是字符串类型,并用双引号包裹;而值可以是以下几种类型之: - 字符串 (String) - 数字 (Number) - 对象 (Object),即嵌套的大括号 `{}` 表达的内容 - 数组 (Array),由方括号 `[]` 定义的系列有序项 - 布尔值 (`true`, `false`) - `null` 示例代码如下所示: ```json { "name": "Alice", "age": 25, "isStudent": false, "courses": ["Math", "Physics"], "address": { "city": "Beijing", "zipCode": 100084 } } ``` 以上例子展示了如何定义个包含简单字段以及复合类型的 JSON 对象[^3]。 --- #### JSON 与 JavaScript 对象的区别 虽然 JSON 的命名来源于 JavaScript,但实际上两者存在细微差异: - 所有的 JSON 属性名都需要加上双引号 `" "` 来标记为字符串型; - 而在纯 JS 对象里,属性名如果仅由字母组成,则可以选择省略引号[^1]。 转换操作可以通过内置函数完成: - 将 JSON 字符串转化为 JS 对象时调用 `JSON.parse()` 方法; - 把 JS 对象序列化JSON 文本则需借助 `JSON.stringify()` 函数实现[^1]。 下面给出具体实例演示这两种情况下的应用方式: ```javascript // JSON字符串转JS对象 const jsonString = '{"name":"Bob","hobby":["reading","traveling"]}'; const objFromJson = JSON.parse(jsonString); console.log(objFromJson.hobby); // 输出 ['reading', 'traveling'] // JS对象转JSON字符串 let jsObj = {title:"Book Title",author:{firstName:"John",lastName:"Doe"}}; let jsonStr = JSON.stringify(jsObj); console.log(jsonStr); // {"title":"Book Title","author":{"firstName":"John","lastName":"Doe"}} ``` --- #### JSON 的应用场景 由于具备良好的兼容性和高效的表现力,JSON 已成为当今互联网领域中最流行的标准化协议之,在以下几个方面尤为突出: - Web API 接口设计——作为前后端交互的主要载体传递参数或者返回结果集给客户端展示[^3]。 - 配置文件管理——像 SAPUI5 这样的框架利用 manifest.json 文件存储元信息指导组件加载流程[^4]。 - 存储本地离线缓存资料等等场景下也十分常见。 --- #### 总结 综上所述,掌握好 JSON基础理论及其实际运用技巧对于从事软件工程相关工作的人员来说至关重要。希望本文能够帮助您建立起关于这主题较为全面的认识!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值