【GSON】GSON学习笔记

GSON

https://github.com/google/gson/blob/master/UserGuide.md

使用

简单类型使用

        // Serialization 序列化的用法 基本类型转换成JSON
        Gson gson = new Gson();
        System.out.println(gson.toJson(2).getClass());//String
        gson.toJson(1);            // ==> 1
        gson.toJson("abcd");       // ==> "abcd"
        gson.toJson(new Long(10)); // ==> 10
        int[] values = { 1 };
        gson.toJson(values);       // ==> [1]
        System.out.println(gson.toJson(values));

        // Deserialization 反序列化,需要传入反序列化目标类型 JSON转换成基本类型
        int one = gson.fromJson("1", int.class);
        Integer one1 = gson.fromJson("1", Integer.class);
        Long one2 = gson.fromJson("1", Long.class);
        Boolean false1 = 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; //transient表明这个字段不序列化
  BagOfPrimitives() {
    // no-args constructor
  }
}

// Serialization
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);  

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

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

对于对象的要求:实体类

  1. 属性使用private修饰
  2. 所有属性默认都会被序列化和反序列化,包括父类的
  3. 使用transient关键字修饰的属性,默认不会被序列化和反序列化
  4. 可以自动处理null值
  5. 序列化成JSON的时候,如果字段为null,默认不输出这个字段
  6. 反序列化的时候,如果JSON中缺失的字段,实体中有,那么就会给实体赋值一个默认的,对象是null,数值型是0,boolean是false
  7. If a field is synthetic, it is ignored and not included in JSON serialization or deserialization.
  8. Fields corresponding to the outer classes in inner classes, anonymous classes, and local classes are ignored and not included in serialization or deserialization

内部类

内部类可以被序列化的前提是,静态内部类,因为反序列化的时候内部类的无参构造问题,要指定外部对象,但是此时是没有的

这个例子就不行

public class A { 
  public String a; 

  class B { 

    public String b; 

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

解决方案有2个:

改成静态内部类

或者

不建议使用:

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);     // ==> [1,2,3,4,5]
gson.toJson(strings);  // ==> ["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的集合类型定义很恶心

在集合使用中的缺陷,可以序列化,但是不能反序列化,因为使用者不知道真正要转换成什么结果集

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

调用toJson(obj)的时候,Gson会调用obj.getClass()来获取要序列化的字段,但是当使用泛型的时候就获取不到了

比如

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

真正的类型应该是Foo,但是这里只能获取到Foo

解决方法

使用TypeToken

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

gson.fromJson(json, 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;
  }
}

序列化的时候直接调用toJson(collection)

反序列化的时候如果直接fromJson(json, Collection.class)是不行的,Gson获取不到集合中的实际数据类型是什么

解决方法:首选方法1

  1. 使用JsonParser转换成List,然后从里面一个个拿出来进行转换
/*
 * Copyright (C) 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.gson.extras.examples.rawcollections;

import java.util.ArrayList;
import java.util.Collection;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;

public class RawCollectionsExample {
  static class Event {
    private String name;
    private String source;
    private Event(String name, String source) {
      this.name = name;
      this.source = source;
    }
    @Override
    public String toString() {
      return String.format("(name=%s, source=%s)", name, source);
    }
  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  public static void main(String[] args) {
    Gson gson = new Gson();
    Collection collection = new ArrayList();
    collection.add("hello");
    collection.add(5);
    collection.add(new Event("GREETINGS", "guest"));
    String json = gson.toJson(collection);
    System.out.println("Using Gson.toJson() on a raw collection: " + json);
      //代码
    JsonParser parser = new JsonParser();
    JsonArray array = parser.parse(json).getAsJsonArray();
    String message = gson.fromJson(array.get(0), String.class);
    int number = gson.fromJson(array.get(1), int.class);
    Event event = gson.fromJson(array.get(2), Event.class);
    System.out.printf("Using Gson.fromJson() to get: %s, %d, %s", message, number, event);
  }
}
  1. 写一个适配器,映射所有的成员到特定的对象实体,不建议使用,这种方法的缺点是它会在Gson中搞乱其他集合类型的反序列化
  2. 写一个适配器,定义好各个成员的类型,比如MyCollectionMemberType ,然后使用fromJson() 方法的时候传入Collection

内置序列化工具和反序列化工具

例子

  1. java.net.URL 用来匹配 "https://github.com/google/gson/"
  2. java.net.URI 用来匹配 "/google/gson/"

自定义序列化和反序列化

通常是:

  1. 定义Json Serializers:需要为对象定义自定义序列化
  2. 定义Json Deserializers:需要定义一个类型的自定义反序列化
  3. 定义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());
Serializers
private class DateTimeSerializer implements JsonSerializer<DateTime> {
  public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
    return new JsonPrimitive(src.toString());
  }
}

针对DateTime类型的序列化,会调用serialize()方法

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

如果要把一个Json的String转换成DataTime类型的时候就会调用

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

这里类型可以改成泛型,就可以作为工具类了

不用写实际的泛型类型就可以用:

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

需要用到实际泛型类型的例子:

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

null的处理

默认null转换成JSON的时候是不写的

这样定义就可以了

Gson gson = new GsonBuilder().serializeNulls().create();
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注解

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"}
排除字段

默认被transient修饰的字段是不序列化的,被static修饰的字段也会被排除掉

包含被static修饰的字段

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

或者

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

这个注解,没有被修饰的字段会被排除,不做序列化和反序列化

自定义排除策略

使用Foo注解的会被排除

String类型的会被排除

最后只留下了long类型的1234

@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字段转换

比如

sampleFieldNameInJava 转换成JSON的时候可以选择

sample_field_name_in_java

或者

SampleFieldNameInJava

例子

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

其他参考资料

https://www.jianshu.com/p/e740196225a4

https://www.jianshu.com/p/c88260adaf5e

https://www.jianshu.com/p/0e40a52c0063

https://www.jianshu.com/p/3108f1e44155

一些补充

多个别名

可以指定多个别名,把几个不同的名字都映射成同一个,在反序列化的时候有用

当多种情况同时出时,以最后一个出现的值为准

@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;
泛型
Gson gson = new Gson(); String jsonArray = "[\"Android\",\"Java\",\"PHP\"]"; String[] strings = gson.fromJson(jsonArray, String[].class); List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());

TypeToken的构造方法是protected修饰的,所以上面才会写成new TypeToken<List<String>>() {}.getType() 而不是 new TypeToken<List<String>>().getType()

看这个会说的更详细

https://www.jianshu.com/p/d62c2be60617

两个有相同字段的类:

public class UserResult { 
    public int code; 
    public String message; 
    public User data; 
} 
public class UserListResult { 
    public int code; 
    public String message; 
    public List<User> data; 
} 
String json = "{..........}"; 
Gson gson = new Gson();

UserResult userResult = gson.fromJson(json,UserResult.class); 
User user = userResult.data; 

UserListResult userListResult = gson.fromJson(json,UserListResult.class); 
List<User> users = userListResult.data;

其实可以简化成:

public class Result<T> {
    public int code;
    public String message;
    public T data;
}

然后直接使用泛型

Type userType = new TypeToken<Result<User>>(){}.getType(); 
Result<User> userResult = gson.fromJson(json,userType); 
User user = userResult.data;

Type userListType = new TypeToken<Result<List<User>>>(){}.getType(); 
Result<List<User>> userListResult = gson.fromJson(json,userListType); 
List<User> users = userListResult.data;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值