Gson介绍


声明:本文转载自https://my.oschina.net/u/3402859/blog/1571502,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

Gson

目前主流的json解析类库有jackson,fastjson,gson,gson的serialization deserialization解析功能无疑是最强大的,基本上完美支持复杂对象的json化和反json化,其他两个库在转换复杂对象时都容易出现问题。如果在不考虑效率的情况下,我强烈推荐使用gson类库。

首先需要添加依赖

//gradle  dependencies {     compile 'com.google.code.gson:gson:2.8.2' }  //maven <dependencies>     <!--  Gson: Java to Json conversion -->     <dependency>       <groupId>com.google.code.gson</groupId>       <artifactId>gson</artifactId>       <version>2.8.2</version>       <scope>compile</scope>     </dependency> </dependencies>

接下来看一些代码例子,gson设计的非常易用,基本上一看就懂。

 

转换基础数据类型

// 创建gson对象,gson对象是内部无状态的,所以创建一个可以多次使用,可以想象成一个转换器 Gson gson = new Gson(); //转换成json只用直接放入对象就行,gson在内部会提取对象的类型信息 gson.toJson(1);            // ==> 1 gson.toJson("abcd");       // ==> "abcd" gson.toJson(new Long(10)); // ==> 10 int[] values = { 1 }; gson.toJson(values);       // ==> [1]  // 反json化,此时需要传入类型参数,因为json中是没有保存关于java类型的信息的 int one = gson.fromJson("1", int.class); Integer one = gson.fromJson("1", Integer.class); Long one = gson.fromJson("1", Long.class); String str = gson.fromJson("\"abc\"", String.class);

 

转换对象类型

当然,gson也支持对对象类型的转换

class BagOfPrimitives {   private int value1 = 1;   private String value2 = "abc";   //使用transient标识的变量不会被json化    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"}

不过注意你不能json化一个具有循环引用的对象,会导致死循环(简单来说就是a有一个变量为b,b也有一个变量为a,此时无论json化谁都会导致死循环)

 

使用gson序列化对象的要点

1.完全可以使用private修饰变量类型,因为gson内部是使用反射来进行json化的,所以private也完全可以读取的到 
2.完全没有必要使用任何的annotations 来标注哪个字段需要被包含(有很多其他库是使用annotations 来标记的),gson默认会序列化当前类中的所有字段(包括他的所有父类) 
3.如果一个字段被标记为transient,他不会被Json化 
4.gson对值为null的字段有很好的支持

 

转换数组类型

Gson gson = new Gson(); int[] ints = {1, 2, 3, 4, 5}; String[] strings = {"abc", "def", "ghi"};  // Serialization gson.toJson(ints);     // ==> [1,2,3,4,5] gson.toJson(strings);  // ==> ["abc", "def", "ghi"]  // Deserialization int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 

 

集合类型

java对集合类型是做了特殊支持的,输出的效果基本和数组一样,除了元素之外不会输出其他的字段例如 size之类的

        Gson gson = new Gson();         Collection<Integer> ints = Arrays.asList(1,2,3,4,5);          // Serialization         String json = gson.toJson(ints);  // ==> json is [1,2,3,4,5]          // Deserialization         //对于泛型类型,因为直接取得的class并不包括泛型类型,所以需要创建type再传入         Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();         Collection<Integer> ints2 = gson.fromJson(json, collectionType);

 

转换泛型类型

因为gson内部是使用getClass来获取类型信息,返回的类型都不包括泛型参数类型,所以直接像之前使用的话,序列化和反序列化都会失败 
(其实不只是gson,java的泛型本身就被吐槽很久了,各种毛病)

class Foo<T> {   T value; } Gson gson = new Gson(); Foo<Bar> foo = new Foo<Bar>(); gson.toJson(foo); //不能成功序列化  gson.fromJson(json, foo.getClass()); // 无法反序列化

如果想要成功序列化带有泛型类型的对象,需要使用Type对象

//注意这里的TypeToken带有{},其实是一个匿名类,因为TypeToken的构造器是非Public的,不能直接构造,我也不太清楚为什么要这样设计 Type fooType = new TypeToken<Foo<Bar>>() {}.getType(); gson.toJson(foo, fooType);  gson.fromJson(json, fooType);

 

转换带有混合类型的集合

有时候你可能会在一个集合中放入不同的类型(虽然我个人强烈不推荐这样写,因为泛型的作用就是为了省掉不必要的cast,你还放入不同类型,这不是自找麻烦吗?) 
但是如果你真的这样写了,gson还是可以帮你转换

Collection collection = new ArrayList(); collection.add("hello"); collection.add(5); collection.add(new Event("GREETINGS", "guest"));  class Event {   private String name;   private String source;   private Event(String name, String source) {     this.name = name;     this.source = source;   } }

你完全可以像直接一样直接序列化这个集合,没有任何问题。 
但是之前也说过,json没有保存关于java对象类型的任何信息(作为一个通用的数据交换格式他这样做是正确的,否则这段json数据就只有gson才能解析了)。 
之前我们在反序列化的同时都要手动传入类型参数,但是作为一个混合集合,显然没有一个所谓的’类型参数’,所以直接反序列化是不可能了。 
如果你想要反序列化这样的json,有以下几种选择。 
1.使用原始的json类库手动解析 
2.注册一个collection 的adapter(我们下一篇会讲),自定义解析过程(还是需要手动解析) 
所以如果想要反序列化这样的集合,无论怎么样都要手写解析,没什么办法,所以最好还是不要在泛型集合里放入不同的类型

 

除了默认的序列化和反序列化行为,gson还允许自定义某些类的序列化和反序列化行为。自定义行为一般用于需要使json对象具有和原来类不同的表示形式,或者默认行为会报错的情况。主要分为三个类 
Json Serializers: 自定义某种对象类型的序列化行为

Json Deserializers: 自定义某种对象类型的反序列化行为

Instance Creators: 自定义某种对象的创建行为

此时我们需要配置gsonbuilder,然后用gsonbuilder来创建gson对象,builder模式大家应该很熟悉了 
代码如下

GsonBuilder gsonBuilder= new GsonBuilder(); gson.registerTypeAdapter(MyType.class, new MySerializer()); gson.registerTypeAdapter(MyType.class, new MyDeserializer()); gson.registerTypeAdapter(MyType.class, new MyInstanceCreator()); Gson gson=gsonBuilder.create();

为某类型注册了自定义的序列化和反序列化行为之后,在转换的过程中一旦遇到该类型的对象,就会调用你注册的行为来序列化。 
这些的类的实现也很容易。

//自定义序列化,需要实现JsonSerializer接口 private class DateTimeSerializer implements JsonSerializer<DateTime> {   public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {     return new JsonPrimitive(src.toString());   } } //自定义序列化,需要实现DateTimeDeserializer 接口 private class DateTimeDeserializer implements JsonDeserializer<DateTime> {   public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)       throws JsonParseException {     return new DateTime(json.getAsJsonPrimitive().getAsString());   } } //自定义对象创建,需要实现InstanceCreator private class MoneyInstanceCreator implements InstanceCreator<Money> {   public Money createInstance(Type type) {     return new Money("1000000", CurrencyCode.USD);   } } 

在我个人使用gson的过程中,我发现有些时候是不得不用自定义序列化和反序列化的,因为在使用默认的行为的情况下,对一些复杂对象经常出错,具体原因我也不知道为什么,很多时候都是出现了死循环,可能java一些类中本身就存在双向引用吧,此外还有一些其他的异常,主要原因都是java一些类本身的继承结构太深了。

 

我们以序列化Image为例(该类直接序列化会报错) 
这个代码是我实际项目中使用的,将javafx的image对象序列化(当然对于图像对象,最后还要使用压缩流来减少体积)

public class GsonImage implements JsonSerializer<Image>,JsonDeserializer<Image> {      @Override     public Image deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)             throws JsonParseException {          Gson gson=new Gson();         byte[] bs=gson.fromJson(json.getAsString(), byte[].class);         BufferedImage image = null;         try {             //从字节数组创建图片             image = ImageIO.read(new ByteArrayInputStream(bs));         } catch (IOException e) {             // TODO Auto-generated catch block             e.printStackTrace();         }         Image image2=SwingFXUtils.toFXImage(image, null);          return image2;     }      @Override     public JsonElement serialize(Image image, Type typeOfSrc, JsonSerializationContext context) {            Gson gson=new Gson();         ByteArrayOutputStream stream=new ByteArrayOutputStream();         BufferedImage bufferedImage=SwingFXUtils.fromFXImage(image, null);         try {             ImageIO.write(bufferedImage, "PNG", stream);         } catch (IOException e) {             // TODO Auto-generated catch block             e.printStackTrace();         }         byte[] imgBytes=stream.toByteArray();         //实际上序列化的是字节数组         String string=gson.toJson(imgBytes);          return new JsonPrimitive(string);     }  }

除了三种自定义行为之外,gsonbuilder还提供了一些配置

setPrettyPrinting() //设置输出格式为可读性优先(默认是体积小优先) serializeNulls() //设置输出null(默认null会被忽略) excludeFieldsWithModifiers()//设置不被包括在序列化中的修饰符(默认为static //transient,但是你可以覆盖设置)

关于gson就介绍到这里

可以访问gson的github帮助页面来获取更多信息 
https://github.com/google/gson/blob/master/UserGuide.md

 

本文发表于2017年11月12日 20:35
(c)注:本文转载自https://my.oschina.net/u/3402859/blog/1571502,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2022 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1