【GSON】翻译Google GSON 的markdown用户说明

写在前面:本文翻译原文为:https://github.com/google/gson/blob/master/UserGuide.md#nested-classes-including-inner-classes

大部分都是用的Google翻译,如果不恰当的地方还请各位指正,共同学习。

Gson用户指南

  1. 概述
  2. Gson的目标
  3. Gson性能和可扩展性
  4. Gson用户
  5. 使用Gson
    • 使用Gson与Maven
    • 原始实例
    • 对象示例
    • 对象的细点
    • 嵌套类(包括内部类)
    • 阵列示例
    • 集合示例
      • 集合限制
    • 序列化和反序列化通用类型
    • 使用任意类型的对象序列化和反序列化集合
    • 内置序列化和反序列化
    • 自定义序列化和反序列化
      • 编写一个串行器
      • 编写解串器
    • 编写实例创建者
      • 用于参数化类型的InstanceCreator
    • 紧凑型 VS 美观打印JSON输出格式
    • 空对象支持
    • 版本控制支持
    • 排除序列化和反序列化领域
      • Java修饰符排除
      • 注解 @Expose
      • 用户定义排除策略
    • JSON字段命名支持
    • 跨自定义序列化器和解串器共享状态
  6. 设计Gson的问题
  7. 未来对Gson的增强

概述

Gson是一个Java库,可用于将Java对象转换为JSON表示形式。 它也可以用于将JSON字符串转换为等效的Java对象。

Gson可以使用任意Java对象,包括您没有源代码的预先存在的对象。

Gson的目标

  • 提供易于使用的机制,如toString()和构造函数(工厂方法)将Java转换为JSON,反之亦然
  • 允许将先前存在的不可修改对象转换为JSON或从JSON转换
  • 允许对象的自定义表示
  • 支持任意复杂的对象
  • 生成紧凑可读的JSON输出

Gson性能和可扩展性

以下是我们在桌面(双Opteron,8GB RAM,64位Ubuntu)上获得的一些指标,这些指标与测试一起运行了许多其他功能。PerformanceTest(性能测试).

  • 字符串支持: 超过25MB的反序列化字符串没有任何问题(详情可见 PerformanceTest 中的 disabled_testStringDeserializationPerformance 方法 )
  • 集合支持:
    • 可序列化140万个对象的集合 (详情可见 PerformanceTest 中的 disabled_testLargeCollectionSerialization 方法)
    • 可反序列化8万7000个对象的集合(详情可见 PerformanceTest 中的disabled_testLargeCollectionDeserialization 方法)
  • Gson 1.4将字节数组和集合的反序列化限制从80KB提高到超过11MB.

注意: 请删除disabled_前缀来运行这些测试。 因为我们使用这个前缀来防止每次运行JUnit测试时运行这些测试。

Gson用户

Gson最初是为了在Google中使用,目前在许多项目中使用。 它现在被一些公共项目和公司使用。

使用Gson

使用的主要类是Gson,您可以通过调用new Gson()创建。 还有一个可用于创建具有各种设置(如版本控制等)的Gson实例的类GsonBuilder

调用Json操作时,Gson实例不会保持任何状态。 因此,您可以自由地重复使用相同的对象进行多个Json序列化和反序列化操作。

使用Gson与Maven

要使用Gon和Maven2 / 3,您可以使用Maven Central中提供的Gson版本,方法是添加以下依赖项:

<dependencies>
    
    <dependency>
      <groupId>com.google.code.gsongroupId>
      <artifactId>gsonartifactId>
      <version>2.8.0version>
      <scope>compilescope>
    dependency>
dependencies>

就是这样,现在你的maven项目是Gson启用的。

原始实例

// 序列化
Gson gson = new Gson();
gson.toJson(1);            // ==> 1
gson.toJson("abcd");       // ==> "abcd"
gson.toJson(new Long(10)); // ==> 10
int[] values = { 1 };
gson.toJson(values);       // ==> [1]

// 反序列化
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);

对象示例

class BagOfPrimitives {
  private int value1 = 1;
  private String value2 = "abc";
  private transient int value3 = 3;
  BagOfPrimitives() {
    // no-args constructor
  }
}

// 序列化
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);  

// ==> json is {"value1":1,"value2":"abc"}

Note that you can not serialize objects with circular references since that will result in infinite recursion.

//反序列化
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
// ==> obj2 is just like obj

关于对象的一些细节

  • 使用私人领域是完美的(并推荐)
  • 没有必要使用任何注释来表示一个字段被包含在序列化和反序列化中。 默认情况下,包含当前类(和所有超级类)中的所有字段.
  • 如果一个字段被标记为transient(默认情况下),它将被忽略,不包括在JSON序列化或反序列化中.
  • 此实现正确处理null
  • 序列化时,从输出中跳过一个空字段
  • 反序列化时,JSON中的缺少条目导致将对象中的相应字段设置为null
  • 如果一个字段是合成的,则它将被忽略,不包括在JSON序列化或反序列化中
  • 与内部类,匿名类和本地类中的外部类对应的字段将被忽略,不包括在序列化或反序列化中

嵌套类(包括内部类)

Gson可以很容易地序列化静态嵌套类。

Gson也可以反序列化静态嵌套类。 然而,Gson可以自动反序列化纯内部类,因为它们的no-args构造函数也需要引用反序列化时不可用的包含Object 。 您可以通过使内部类静态或为其提供自定义InstanceCreator来解决此问题。 这是一个例子:

public class A { 
  public String a; 

  class B { 

    public String b; 

    public B() {
      // No args constructor for B
    }
  } 
}

注意: 上面的class B不能(在默认情况下)用Gson序列化。
Gson不能反序列化{"b":"abc"} 由于class B是内部类,所以进入B的一个实例。 如果将它定义为静态类B,则Gson将能够反序列化字符串。 另一个解决方案是为B编写一个自定义实例创建者。

public class InstanceCreatorForB implements InstanceCreator<A.B> {
  private final A a;
  public InstanceCreatorForB(A a)  {
    this.a = a;
  }
  public A.B createInstance(Type type) {
    return a.new B();
  }
}

以上是可能的,但不推荐。

阵列示例

Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};

// 序列化
gson.toJson(ints);     // ==> [1,2,3,4,5]
gson.toJson(strings);  // ==> ["abc", "def", "ghi"]

// 反序列化
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 
// ==> ints2 will be same as ints

我们还支持多维数组,具有任意复杂的元素类型。

集合示例

Gson gson = new Gson();
Collection ints = Lists.immutableList(1,2,3,4,5);

// 序列化
String json = gson.toJson(ints);  // ==> json is [1,2,3,4,5]

// 反序列化
Type collectionType = new TypeToken>(){}.getType();
Collection ints2 = gson.fromJson(json, collectionType);
// ==> ints2 is same as ints

相当可怕:注意我们如何定义集合的类型。
不幸的是,没有办法在Java中解决这个问题。

集合限制

Gson可以序列化任意对象的集合,但不能反序列化它,因为用户没有办法指示生成的对象的类型。 相反,在反序列化时,集合必须是特定的通用类型。
这是有道理的,并且在遵循良好的Java编码实践时很少出现问题。

序列化和反序列化通用类型

当你调用 toJson(obj), Gson 会调用 obj.getClass() 来获取字段的序列化信息。类似地,你通常可以通过fromJson(json, MyClass.class)方法来传递 MyClass.class 对象。如果对象是非泛型类型,则此操作正常。 但是,如果对象是通用类型,那么由于Java类型擦除,通用类型信息会丢失。 这是一个例子,说明一点:

class Foo {
  T value;
}
Gson gson = new Gson();
Foo foo = new Foo();
gson.toJson(foo); // 不能正确序列化foo.value

gson.fromJson(json, foo.getClass()); // 无法将foo.value反序列化为Bar

上述代码无法将值解释为类型栏,因为Gson调用list.getClass() 获取它的类信息,但这个方法返回一个raw类Foo.class。 这意味着Gson无法知道这是 Foo,类型的对象,而不仅仅是简单的Foo

您可以通过为通用类型指定正确的参数化类型来解决此问题。 您可以使用TypeToken类来执行此操作。

Type fooType = new TypeToken>() {}.getType();
gson.toJson(foo, fooType);

gson.fromJson(json, fooType);

用于获取fooType的成语实际上定义了一个匿名的本地内部类,它包含一个返回完全参数化类型的方法getType()

使用任意类型的对象序列化和反序列化集合

有时您正在处理包含混合类型的JSON数组。 例如:
['hello',5,{name:'GREETINGS',source:'guest'}]

包含这个的等效的Collection是:

Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));

其中Event类定义为:

class Event {
  private String name;
  private String source;
  private Event(String name, String source) {
    this.name = name;
    this.source = source;
  }
}

您可以使用Gson序列化集合,而无需执行任何特定操作:toJson(collection)会写出所需的输出。

然而,使用fromJson(json,Collection.class)'进行反序列化将不起作用,因为Gson无法知道如何将输入映射到类型。 Gson要求您在fromJson()`中提供一个集合类型的泛型版本。 所以你有三个选择:

  1. 使用Gson的解析器API(低级流解析器或DOM解析器JsonParser)来解析数组元素,然后在每个数组元素上使用Gson.fromJson()。这是首选方法。 这里有一个例子 说明了如何做到这一点。
  2. Collection.class注册一个类型适配器,查看每个数组成员并将它们映射到适当的对象。 这种方法的缺点在于它会使Gson中其他收集类型的反序列化。

  3. MyCollectionMemberType注册一个类型适配器,并使用fromJson()Collection

    只有当阵列显示为顶级元素,或者您可以将保存集合的字段类型更改为Collection 类型时,此方法才是实用的。

内置序列化器和反序列化器

Gson已经为常用的类内置了序列化器和反序列化器,其默认表示可能不合适。
这是一个这样的类的列表:

  1. "java.net.URL"将其与"https://github.com/google/gson/"等字符串进行匹配
  2. java.net.URI 将与 "/google/gson/" 等字符串进行匹配

您还可以在本页中找到一些常用类的源代码,如JodaTime

自定义序列化和反序列化

有时,默认表示不是你想要的。处理库类(DateTime等)通常是这种情况。

Gson允许您注册自己的自定义序列化器和反序列化器。这通过定义两部分来完成:

  • Json 序列化:需要定义一个对象的自定义序列化
  • Json 反序列化 : 需要定义一个类型的自定义反序列化

  • 实例创建者:如果no-args构造函数可用或反序列化器已注册,则不需要

GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(MyType2.class, new MyTypeAdapter());
gson.registerTypeAdapter(MyType.class, new MySerializer());
gson.registerTypeAdapter(MyType.class, new MyDeserializer());
gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());

registerTypeAdapter 调用检查,如果类型适配器实现了多个这些接口,并为其注册。

编写一个序列化器

以下是JodaTimeDateTime类的自定义序列化程序的一个示例。

private class DateTimeSerializer implements JsonSerializer<DateTime> {
  public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
    return new JsonPrimitive(src.toString());
  }
}

在序列化期间,当它运行到DateTime对象时,Gson调用serialize()

编写序列化器

这是一个如何为JodaTime DateTime类编写自定义解串器的示例。

private class DateTimeDeserializer implements JsonDeserializer<DateTime> {
  public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException {
    return new DateTime(json.getAsJsonPrimitive().getAsString());
  }
}

当需要将JSON字符串片段反序列化为DateTime对象时,Gson调用deserialize

序列化器和反序列化器的更好之处

通常,您想要为对应于原始类型的所有通用类型注册单个处理程序

  • 例如,假设您有一个用于id表示/翻译的Id类(即内部对外部表示)。
  • 对于所有通用类型,Id 类型具有相同的序列化
    • 基本上写出id值
  • 反序列化非常相似但不完全相同
    • 需要调用新的Id(Class ,String)返回一个Id 的实例

Gson支持为此注册一个处理程序。您也可以为特定的通用类型注册一个特定的处理程序(例如Id 需要特殊处理)。

编写实例创建者

在反序列化对象时,Gson需要创建一个类的默认实例。
用于序列化和反序列化的良好的类应该有一个无参数的构造函数。

  • 无论公共还是私人都不重要

通常,当您处理不定义无参数构造函数的库类时,需要实例创建者

实例创建者示例

private class MoneyInstanceCreator implements InstanceCreator<Money> {
  public Money createInstance(Type type) {
    return new Money("1000000", CurrencyCode.USD);
  }
}

类型可以是相应的通用类型

  • 非常有用的是调用需要特定通用类型信息的构造函数
  • 例如,如果Id类存储正在创建Id的类

用于参数化类型的InstanceCreator

有时您尝试实例化的类型是参数化类型。一般来说,这不是一个问题,因为实际的实例是原始类型。这是一个例子:

class MyList extends ArrayList {
}

class MyListInstanceCreator implements InstanceCreator> {
    @SuppressWarnings("unchecked")
  public MyList createInstance(Type type) {
    // No need to use a parameterized list since the actual instance will have the raw type anyway.
    return new MyList();
  }
}

但是,有时您需要根据实际的参数化类型创建实例。在这种情况下,您可以使用传递给createInstance方法的type参数。这是一个例子:

public class Id<T> {
  private final Class classOfId;
  private final long value;
  public Id(Class classOfId, long value) {
    this.classOfId = classOfId;
    this.value = value;
  }
}

class IdInstanceCreator implements InstanceCreator> {
  public Id createInstance(Type type) {
    Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments();
    Type idType = typeParameters[0]; // Id has only one parameterized type T
    return Id.get((Class)idType, 0L);
  }
}

在上面的例子中,无法实际传递参数化类型的实际类型,不能创建Id类的实例。我们通过使用传递的方法参数type来解决这个问题。这种情况下的type对象是Id 的Java参数化类型表示,其中实际的实例应该被绑定到Id 。由于Id类只有一个参数化类型参数T,所以我们使用getActualTypeArgument()返回的类型数组的第零个元素,在这种情况下它将保存 Foo.class

紧凑型 VS 美观打印JSON输出格式

由Gson提供的默认JSON输出是一个紧凑的JSON格式。这意味着在输出JSON结构中不会有任何空格。因此,在JSON输出中的数组之间,字段名称及其值,对象字段和对象之间不会有空格。同样,输出中将忽略null字段(注意:空值仍将包含在对象的集合/数组中)。有关配置Gson以输出所有空值的信息,请参阅Null Object Support部分。

如果要使用“漂亮打印”功能,则必须使用GsonBuilder配置Gson实例。 JsonFormatter不会通过我们的公共API公开,所以客户端无法配置JSON输出的默认打印设置/页边距。现在,我们只提供一个默认的JsonPrintFormatter,它的默认行长度为80个字符,2个字符的缩进和4个字符的右边距。

以下是一个示例,显示如何配置Gson实例以使用默认的JsonPrintFormatter而不是JsonCompactFormatter

Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonOutput = gson.toJson(someObject);

空对象支持

在Gson中实现的默认行为是忽略null对象字段。这允许更紧凑的输出格式;然而,客户端必须为这些字段定义一个默认值,因为JSON格式被转换回其Java形式。

以下是配置Gson实例以输出null的方式:

Gson gson = new GsonBuilder().serializeNulls().create();

注意:当使用Gson序列化nulls时,它将向JsonElement结构添加一个JsonNull元素。因此,该对象可以用于自定义序列化/反序列化。

以下是一个例子:

public class Foo {
  private final String s;
  private final int i;

  public Foo() {
    this(null, 5);
  }

  public Foo(String s, int i) {
    this.s = s;
    this.i = i;
  }
}

Gson gson = new GsonBuilder().serializeNulls().create();
Foo foo = new Foo();
String json = gson.toJson(foo);
System.out.println(json);

json = gson.toJson(null);
System.out.println(json);

输出为:

{"s":null,"i":5}
null

版本控制支持

可以使用@Since注释来维护同一对象的多个版本。此注释可用于类,字段,以及将来的版本中的方法。为了利用此功能,您必须将Gson实例配置为忽略大于某个版本号的任何字段/对象。如果在Gson实例上没有设置任何版本,那么无论版本如何,它都会对所有字段和类进行序列化和反序列化。

public class VersionedClass {
  @Since(1.1) private final String newerField;
  @Since(1.0) private final String newField;
  private final String field;

  public VersionedClass() {
    this.newerField = "newer";
    this.newField = "new";
    this.field = "old";
  }
}

VersionedClass versionedObject = new VersionedClass();
Gson gson = new GsonBuilder().setVersion(1.0).create();
String jsonOutput = gson.toJson(someObject);
System.out.println(jsonOutput);
System.out.println();

gson = new Gson();
jsonOutput = gson.toJson(someObject);
System.out.println(jsonOutput);

输出为:

{"newField":"new","field":"old"}

{"newerField":"newer","newField":"new","field":"old"}

排除序列化和反序列化领域

Gson支持排除顶级类,字段和字段类型的大量机制。以下是允许字段和类别排除的可插拔机制。如果以下机制都不满足您的需求,那么您可以随时使用定制序列化器和解串器。

Java修饰符排除

默认情况下,如果将一个字段标记为transient,那么它将被排除。同样,如果一个字段被标记为static,则默认情况下将被排除。如果要包括一些暂时的字段,则可以执行以下操作:

import java.lang.reflect.Modifier;
Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC)
    .create();

注意:您可以给excludeFieldsWithModifiers方法的任意数量的Modifier常量。例如:

Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
    .create();

Gson’s @Expose

此功能提供了一种方法,您可以标记要排除的对象的某些字段,以便对JSON进行序列化和反序列化。要使用此注释,您必须使用new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()创建Gson。创建的Gson实例将排除未标记“@ Expose”注释的类中的所有字段。

用户定义排除策略

如果上述排除字段和类类型的机制不适合您,那么您可以随时编写自己的排除策略并将其插入到Gson中。有关详细信息,请参阅ExclusionStrategy

以下示例显示如何排除标记有特定@ Foo注释的字段,并排除String类的顶级类型(或声明的字段类型)。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Foo {
  // Field tag only annotation
}

public class SampleObjectForTest {
  @Foo private final int annotatedField;
  private final String stringField;
  private final long longField;
  private final Class clazzField;

  public SampleObjectForTest() {
    annotatedField = 5;
    stringField = "someDefaultValue";
    longField = 1234;
  }
}

public class MyExclusionStrategy implements ExclusionStrategy {
  private final Class typeToSkip;

  private MyExclusionStrategy(Class typeToSkip) {
    this.typeToSkip = typeToSkip;
  }

  public boolean shouldSkipClass(Class clazz) {
    return (clazz == typeToSkip);
  }

  public boolean shouldSkipField(FieldAttributes f) {
    return f.getAnnotation(Foo.class) != null;
  }
}

public static void main(String[] args) {
  Gson gson = new GsonBuilder()
      .setExclusionStrategies(new MyExclusionStrategy(String.class))
      .serializeNulls()
      .create();
  SampleObjectForTest src = new SampleObjectForTest();
  String json = gson.toJson(src);
  System.out.println(json);
}

输出为:

{"longField":1234}

JSON字段命名支持

Gson支持一些预定义的字段命名策略,以将标准Java字段名称(即,以小写—sampleFieldNameInJava开头的骆驼套装名称)转换为Json字段名称(即sample_field_name_in_javaSampleFieldNameInJava)。有关预定义的命名策略的信息,请参阅FieldNamingPolicy类。

它还具有基于注释的策略,允许客户端在每个字段的基础上定义自定义名称。请注意,基于注释的策略具有字段名称验证,如果提供无效字段名称作为注释值,则会引发运行时异常。

以下是如何使用Gson命名策略功能的示例:

private class SomeObject {
  @SerializedName("custom_naming") private final String someField;
  private final String someOtherField;

  public SomeObject(String a, String b) {
    this.someField = a;
    this.someOtherField = b;
  }
}

SomeObject someObject = new SomeObject("first", "second");
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
String jsonRepresentation = gson.toJson(someObject);
System.out.println(jsonRepresentation);

输出为:

{"custom_naming":"first","SomeOtherField":"second"}

如果您需要自定义命名策略(参见此讨论),则可以使用@SerializedName注释。

跨自定义序列化器和解串器共享状态

有时您需要在自定义序列化器/解串器之间共享状态(参见此讨论)。您可以使用以下三种策略来实现:

  1. 将共享状态存储在静态字段中
  2. 将序列化器/反序列化声明为父类型的内部类,并使用父类型的实例字段来存储共享状态
  3. 使用Java的ThreadLocal

1和2不是线程安全选项,而3是。

此外,Gson的对象模型和数据绑定,您可以使用Gson从stream读取和写入。您还可以组合流和对象模型访问,以获得最佳的两种方法。

设计Gson的问题

有关设计Gson时遇到的问题的讨论,请参阅Gson设计文档。它还包括将Gson与可用于Json转换的其他Java库进行比较。

未来对Gson的增强

有关建议的增强功能的最新列表,或者如果您想建议新增功能,请参阅项目网站下的问题部分。

你可能感兴趣的:(json)