Android Gson解析Json(常规使用)

时间:2021-06-08 10:26:51

gson是一个java库,用作java对象和json表达式之间的转换。gson可以处理任何Java对象,包括已经存在的、你没有源代码的对象。gson有很多的优势,比如它强大的功能:1.反序列化25M以上的字符串没有任何问题。2.它可以序列化一个有140万个对象的集合,3.反序列话87000个对象的集合,4.gson1.4 提高了反序列化字节数组的限制,从87KB提高到了11M.此外。

在刚开始学习Gson的时候,我试图在百度上寻找资料。是的,资料很多,但都是零零散散的资料,并不是我想要的系统的学习资料。浏览了下Gson官网,看到有个Gson User Guilde,我觉得从这里切入是不错的选择,虽然我英语很烂,六级都没过,但是我发现阅读英文资料好像更容易读懂一点,貌似编程这东西用英语表示能表示的更加具体和清晰。

1.下载Gson与例程的使用

  1.下载gson

 下载地址:点击下载gson,下载好以后把jar文件放入libs目录下,然后在android studio中,在jar文件上右键,选择add as library

  2.使用源码中的例子

  json源码里面有个examples目录,这个目录下有个android的例子。这个例子界面有点丑,如下:

Android Gson解析Json(常规使用)

但是使用这个例子可以帮你省去一些时间,在使用这个工程的时候,注意,直接用android studio打开后是无法使用的,但是你可以用android studio 下的 File->New->import Project功能,它会为你生成android studio可以直接运行的项目。如果这个过程出了问题,应该是gradle的某些配置不对,如果你遇到类似的问题,可以参考下我的这两篇文章:

理解与配置android studio中的gradle

详细配置android studio中的gradle


2 使用Gson

  为了使用Gson,首先需要获得Gson的一个实例,有两种方法获得它:1. new Gson(),2. GsonBuilder,使用这个类也可以创建一个Gson实例,并且你可以做一些设置,比如版本控制等等。

2.1 new Gson()或者GsonBuilder. create()?

使用new Gson会创建一个默认参数的Gson对象,你基本不需要做什么,家乡下面这样:

Gson gson = new Gson();

如果你想做细致的控制,可以像下面这样:

Gson gson = new GsonBuilder()  
.registerTypeAdapter(Id.class, new IdTypeAdapter())
.enableComplexMapKeySerialization()
.serializeNulls()
.setDateFormat(DateFormat.LONG)
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.setPrettyPrinting()
.setVersion(1.0)
.create();
Gson真的设计的非常简练,使用Gson转换java对象与json表达式貌似主要涉及两个方法,而且很多情况下这个两个方法就能搞定一切,这两个方法就是toJson()和fromJson()。


2.2使用new Gson序列化和反序列化原始类型的例子:

<span style="font-family:SimSun;font-size:14px;">    // Serialization
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]

// 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);</span>

2.3使用new Gson序列化和反序列化一个对象

<span style="font-family:SimSun;font-size:14px;">    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"}
// Deserialization
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
// ==> obj2 is just like obj</span>
注意:你不能序列化一个循环引用,这将会导致无限循环。

  关于序列化与反序列化的对象的一些细节:

  • 推荐使用私有属性。
  • 没有必要使用注释表明某个字段将被用作序列化和反序列化,所有的字段都会被默认的包含进来。
  • 如果一个字段被声明为transient,那么它将不会被用作序列化和烦序列化。
  • 能正确处理null.
  • 序列化的时候null的字段会被跳过。
  • 反序列化时,失踪的条目,其对象对应的字段会被设置位null。
  • synthetic修饰的字段,在序列化和反序列的过程中会被忽略。
  • 内部类中对应与外部类的字段,匿名类,本地类会被忽略。

2.4 嵌套类(nested classes)

Gson可以轻易的序列化静态嵌套类。

Gson也可以反序列化静态嵌套类。Gson不能自动反序列化纯内部类,因为他们的无参构造函数也需要包含对象的引用,而这个引用在反序列化的时候不可用。你可以使用两种方法解决这个问题:

  1. 声明内部类为static
  2. 提供一个内部类的实例构建器

比如说一下例子:

public class A { 
public String a;

class B {

public String b;

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

在这个例子中,不能使用 {"b":"abc"}反序列化生成B的实例。把class B声明位静态是简单的解决方法,此为,你还可以给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();
}
}

这种方法虽然可行,但并不推荐。

2.5 数组的序列化与反序列化

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也支持多维数组。

2.6 集合的序列化与反序列化


<span style="font-family:SimSun;font-size:14px;">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</span>

很可怕的一点:注意这里是怎么定义集合类型的?不幸的是,在java中,我们无法绕过这一点。


2.7 序列化与反序列化泛型

当我们调用toJson(obj),Gson会调用obj.getClass()来获取需要序列化的字段。同样的,我们可以传入MyClass.class对象给fromJson(json,MyClass.class),当对象不是泛型的时候,一切都工作良好,可是,如果对象是泛型,由于java类型擦除机制,泛型的信息会丢失。下面有一个解释这点的例子:

<span style="font-family:SimSun;font-size:14px;">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</span>
上面的代码无法过去Bar类型的值,所以会解析失败。因为Gson会调用list.getClass()获得类信息,但是这个方法返回了一个原始的值,Foo.class,也就是说我们无法知道类的类型是一个Foo<Bar>类型,而不是Foo类型。

你可以解决这个问题,使用指定泛型的正确的参数化类型:

<span style="font-family:SimSun;font-size:14px;">Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);

gson.fromJson(json, fooType);</span>

这里通过将要过去类型的泛型类型,传递给一个TypeToken的匿名子类的getType方法,从而获得一个完全的参数化类型。

2.8 序列化与反序列化任意类型的集合

有时候需要处理包含多种类型的json数组,比如:

<span style="font-family:SimSun;font-size:14px;">{name:'GREETINGS',source:'guest'}]</span>
与之等价的集合是这样的:

<span style="font-family:SimSun;font-size:14px;">Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));</span>

这个Event的定义是这样的:

<span style="font-family:SimSun;font-size:14px;">class Event {
private String name;
private String source;
private Event(String name, String source) {
this.name = name;
this.source = source;
}
}</span>
你可以序列化集合使用toJson(connect),这没有任何问题,可以的要期望的结果

但是,如果是使用fomJson进行反序列化的话就不能进行,因为Gson不知道怎样把输入映射为类型。所以你有三个选择解决这个问题:

  1. 使用Gson的解析器API(一个低水平的流解析器或者Dom解析器JsonParser)来解析数组元素,然后对数组的每一个元素使用Gson.formJson()来获得Java对象。这是推荐的方式。这里有个例子,只贴出核心代码:
    <span style="font-family:SimSun;font-size:14px;">    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);</span>

  2. 位Collection.class注册一个适配器类,用来观察每一个数组成员,并把他们映射为正确的类型。这种方法的缺点是它会搞砸Gson中其他集合类型的反序列化。
  3. 位MyCollectionMemberType注册一个适配器类型,并使用对Collection<MyCollectionMemberType>使用fromJson()方法。

    Gson User Guilde内容较多,这里就只介绍它的常规使用,其他的内容会在后续的文章中继续探究。