Google Gson官方教程

英文官方文档:

https://sites.google.com/site/gson/gson-user-guide

概述

Gson是一个Java库,可用于将Java对象转换为JSON表示。它也可以被用来将JSON字符串转换成一个等效的Java对象。Gson是一个开源项目托管在http://code.google.com/p/google-gson,Gson可以处理任意Java对象包括已存在的对象,这种处理你没有源代码。

Gson的目标

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

Gson性能和可扩展性

这里有一些指标,我们获得了在台式机(双皓龙处理器,8 gb的内存,64位Ubuntu)运行许多其他东西一样的测试。你可以通过使用类PerformanceTest重新运行这些测试。

  • 字符串:超过25 mb的反序列化的字符串没有任何问题(见disabled_testStringDeserializationPerformance方法性能测试)
  • Large collections: 序列化140万对象的集合(见disabled_testLargeCollectionSerialization方法性能测试);反序列化87000对象的集合(见disabled_testLargeCollectionDeserialization性能测试);
  • Gson 1.4提高了字节数组和集合的反序列化限制从80 kb在11 mb。

注意:删除disabled_前缀来运行这些测试。我们使用这个前缀,以防止我们每次运行junit测试运行这些测试。

使用Gson

主要的类使用Gson你可以创建通过调用新的Gson(),还有一个类GsonBuilder可用,可以用于创建一个Gson实例与版本控制等各种设置等等。Gson实例调用Json操作时不维持任何状态。所以,你可以自由地重用相同的对象为多个Json序列化和反序列化操作。

简单例子

(Serialization)
Gson gson = new Gson();
gson.toJson(1);            ==> prints 1
gson.toJson("abcd");       ==> prints "abcd"
gson.toJson(new Long(10)); ==> prints 10
int[] values = { 1 };
gson.toJson(values);       ==> prints [1]

(Deserialization)
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
  }
}

(Serialization)
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. 

(Deserialization)
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);   
==> obj2 is just like obj

小细节

  • 最好(和推荐)去使用私有字段
  • 不需要使用任何注释指示字段包含序列化和反序列化。当前类中的所有字段(和所有的超类)都包含在默认情况下。
  • 如果一个字段标记为瞬态,(默认情况下)被忽略,而不是包含在JSON序列化或反序列化。
  • 这个实现正确处理nulls
    - 序列化时,跳过一个空字段的输出
    -反序列化时,在JSON结果对象中失踪的条目设置相应的字段为null

  • 如果一个字段是合成的,它将被忽略,而不是包含在JSON序列化或反序列化

  • 字段对应于外部类的内部类,匿名类,和当地的类被忽略,而不是包含在序列化或反序列化

嵌套类(包括内部类)

Gson可以快速容易的序列化静态嵌套类。
Gson也可以反序列化静态嵌套类。然而,Gson不能自动反序列化纯内部类,因为他们的无参构造函数也需要包含对象的引用,反序列化的时候不可用。你可以解决这个问题,使静态内部类或通过提供一个自定义InstanceCreator。这里有一个例子:

public class A { 
  public String a; 

  class B { 

    public String b; 

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

注:class B不能被Gson(在默认情况下)序列化。
在B是个内部类的情况下,Gson不能反序列化{“b”:“abc”}到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"};

(Serialization)
gson.toJson(ints);     ==> prints [1,2,3,4,5]
gson.toJson(strings);  ==> prints ["abc", "def", "ghi"]

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

同时支持多维数组,任意复杂的元素类型。

集合的例子

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

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

(Deserialization)
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
ints2 is same as ints

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

集合的局限性

  • 可以序列化任意对象的集合,但不能反序列化
    -因为没有为用户显示生成的对象的类型

  • 反序列化时,集合必须是一个特定的泛型类型

所有这一切是有道理的,良好的Java编码实践很少有问题。

泛型类型的序列化和反序列化

当你调用toJson(obj)时,Gson调用obj.getClass()获取信息字段序列化。类似的,你可以通常通过在fromJson(json,MyClass.class)方法中的MyClass.class对象获取。如果对象不是泛型处理会很好。然而,如果是泛型类型的对象,那么泛型类型信息会丢失,因为Java的类型擦除。这里有一个例子说明这一点:

class Foo<T> {
  T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // May not serialize foo.value correctly

gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar

上面的代码无法解码作为Bar类型的数值,因为Gson调用list.getClass()来获得它的类信息,但是这个方法返回一个原始类,Foo.class。这意味着Gson没有办法知道,这是一个Foo< Bar >类型的对象而不是普通的Foo。
你可以通过为你的泛型指定正确的参数类型来解决问题。你可以通过使用TypeToken类。

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

gson.fromJson(json, fooType);

看起来它用来得到fooType实际上定义了一个匿名本地内部类包含一个方法 getType(),它返回完全参数化的类型。

任意类型的对象集合进行序列化和反序列化

有时你正在处理JSON数组,其中包含混合类型。例如:

['hello',5,{name:'GREETINGS',source:'guest'}]

相当于集合包含这些内容:

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)会写出所需的输出。然而在Gson没有办法知道如何映射的输入类型时反序列化 fromJson(json, Collection.class)不会工作。Gson要求你在fromJson中提供一个集合类型的泛型版本。所以,你有三个选择:

选项1 : 使用Gson解析器API(低级流解析器或DOM解析器JsonParser)来解析数组元素,然后使用Gson.fromJson()数组的每个元素。这是首选的方法。下面是一个示例,该示例演示了如何做到这一点。
选项2 : 注册一个Collection.class的类型适配器,用来查看每个数组的成员并将它们映射到适当的对象。这种方法的缺点是,它将在Gson中把其他集合类型的反序列化弄错。
选项3 : 注册一个类型适配器MyCollectionMemberType和使用Collection< MyCollectionMemberType >的fromJson 。

这种方法在只有数组出现作为一个顶级元素,或者你可以修改字段类型来保持 Collection< MyCollectionMemberTyep>的集合类型是实用的。

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

Gson内置的常用类的序列化器和反序列化器的默认表示可能是不合适的。
你还可以找到源代码JodaTime等一些常用的类在这个页面

自定义序列化和反序列化

有时候默认表示不是你想要的,在处理库类(DateTime等)这是常有的事。Gson允许你注册你的自定义的序列化器和反序列化器,这是通过定义两个部分:

  • Json 序列化:需要定义一个自定义序列化对象
  • Json反序列化对象:需要定义一个自定义反序列化的类型
  • Instance Creators:如果无参构造函数可用或用反序列化器注册则不需要
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调用检查适配器实现这些接口的多个类型和注册它。

写一个序列化器

这里有一个例子如何编写一个定制的序列化器JodaTime DateTime类。

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

当它运行到一个DateTime对象序列化期间Gson会调用toJson()。

写一个反序列器

这里的一个例子如何编写一个定制的反序列化器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());
  }
}

Gson调用fromJson()时,它需要一个JSON字符串片段反序列化为DateTime对象。

写一个实例创建者

当反序列化一个对象时,Gson需要创建一个默认的类实例。
正确的行为是意味着序列化和反序列化都会有一个无惨的构造器,无所谓公有还是私有。

实例创建者的例子

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

InstanceCreator参数化类型

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

class MyList<T> extends ArrayList<T> {
}

class MyListInstanceCreator implements InstanceCreator<MyList<?>> {
    @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,这里有一个例子:

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

class IdInstanceCreator implements InstanceCreator<Id<?>> {
  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);
  }
}

空对象支持

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

这里是你如何配置一个Gson实例用来输出null:

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

注意:当Gson序列化为null时,它会在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);

======== OUTPUT ========
{"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);

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

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

不包括字段序列化和反序列化

Java Modifier Exclusion
默认情况下,如果一个字段标记为瞬态,它将被排除在外。如果一个字段标记为“静态”,那么在默认情况下将被排除在外。如果你想包括一些瞬态字段,那么你可以做以下几点:

import java.lang.reflect.Modifier;

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

注意:你可以使用任意数量的Modifier 常量到 “excludeFieldsWithModifiers” 方法中。例如:

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

Gson’s @Expose
这个特性提供了一种方法,你可以标记某些字段的对象排除考虑JSON序列化和反序列化。要使用该注释,你必须创建通过使用 new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()的Gson。Gson创建实例将在一个类排除那些没有标记@Expose注释标记的所有字段。

用户定义的排斥策略

下面的例子显示了如何排除字段标记与一个特定的“@Foo”注释和排除顶级类型(或宣布字段类型)的字符串类。

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

======== OUTPUT ========
{"longField":1234}

JSON支持字段命名

下面是一个例子,如何使用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);

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

转载请注明出处

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值