Gson使用回顾

早些时候,我开发天气模块的时候,服务器来的数据比较简单,我就直接通过JSONObject解析了。到了后来,增加了新闻模块,那使用JSONObject显然就不能满足需求了,所以我选择了Gson。在手机App开发上,这些东西都已经用烂了,但是在车联网上,这东西还不怎么用,因为车联网也就是最近2年才火起来的。

我用到的大都是Serialization(序列化)的操作,反序列化(Deserialization)的操作不多,所以我主要说一说序列化操作。

注解的使用:

public @interface Expose

用来指示该类的 JSON应该是序列化或者反序列。使用该注解,除非使用 GsonBuilder 生成 Gson并调用 GsonBuilder. excludeFieldsWithoutExposeAnnotation ()方法, 否则此注释无效。例如:

public class User {

   @Expose 
   private String firstName;

   @Expose(serialize = false) 
   private String lastName;

   @Expose (serialize = false, deserialize = false) 
   private String emailAddress;

   private String password;

 }

如果通过Gson gson =new Gson();
gson.toJson(); gson.fromJson();的方式来解析Json,那么上面那四个字段都会进行序列化或反序列化。如果通过
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
的方式的话解析Json的话,那么将会排除password字段,因为该字段没有标记@Expose;lastNameemailAddress也不能被序列化,同时emailAddress也不可以被反序列化。

public @interface JsonAdapter

在字段或者类上面添加的注解,用于指定对应TypeAdapter或者TypeAdapterFactory,或者实现一个或两个 JsonDeserializerJsonSerializer的类。
加了这个注解之后,Gson就会自动调用它来序列化或者反序列化。
比如用户在实现了TypeAdapter之后,需要调用GsonBuilder gsonBuilder = new GsonBuilder();gsonBuilder .registerTypeAdapter();,来解析Json。那么如果加了这个注解,就不需要配置GsonBuilder了,因为注解的优先级高。例如:

 //类的注解
 @JsonAdapter(UserJsonAdapter.class)
 public class User {
   public final String firstName, lastName;
   private User(String firstName, String lastName) {
     this.firstName = firstName;
     this.lastName = lastName;
   }
 }
public class UserJsonAdapter extends TypeAdapter<User> {
...省略代码...
}

//字段的注解
private static final class Gadget {
   @JsonAdapter(UserJsonAdapter2.class)
   final User user;
   Gadget(User user) {
     this.user = user;
   }
 }
public @interface SerializedName

这个注解是最最好用的一个。该注解能指定该字段在JSON中对应的字段名称。
举个例子,比如服务端给你返回了一个这样的字段。
{
“num”: “2343567”,
“title”: “Java”
}
我不喜欢这个title字段,这个看起来有些抽象,怎样简单的定义这个字段,而且还可以定制自己喜欢的单词。对,就是这个注解可以帮你。比如我们可以定义:
@SerializedName(“title”)
private String name;

在定义@SerializedName的时候有两个key可以供我们选择。
一个是value:当它被序列化或反序列化时所需要的字段名。
另一个是alternate:在反序列化时字段的其他名称。

完整的例子:

 public class MyClass {
   @SerializedName("name") 
   String a;

   @SerializedName(value="name1", alternate={"name2", "name3"}) 
   String b;

   String c;

   public MyClass(String a, String b, String c) {
     this.a = a;
     this.b = b;
     this.c = c;
   }
 }

MyClass target = new MyClass("v1", "v2", "v3");
 Gson gson = new Gson();
 String json = gson.toJson(target);
 System.out.println(json);

 ===== OUTPUT =====
 {"name":"v1","name1":"v2","c":"v3"}

当反序列化时,注释中指定的所有值将被反序列化到字段中。例如:

MyClass target = gson.fromJson("{'name1':'v1'}", MyClass.class);
   assertEquals("v1", target.b);
   target = gson.fromJson("{'name2':'v2'}", MyClass.class);
   assertEquals("v2", target.b);
   target = gson.fromJson("{'name3':'v3'}", MyClass.class);
   assertEquals("v3", target.b);

关于assertEquals,断言的意思。我们在写Android测试用例的时候,会使用这个方法。判断用例执行完成后得到的值是否与预期值一致。比如Assert.assertEquals(1,2);那么系统会报异常junit.framework.AssertionFailedError: expected:<1> but was:<2>。

public @interface Since

这个注解的意思是:在…版本之后无效(不包括当前版本)
当定义了Gson gson = new GsonBuilder().setVersion(1.0).create()之后:

public class User {

   private String firstName;
   private String lastName;

   @Since(0.8) 
   private String emailAddress;

   @Since(1.1) 
   private String password;

 }

这时使用Gson解析的结果将会排除password字段。

public @interface Until

该注解的意思是:在…版本之前无效(包括当前版本)。
当定义了Gson gson = new GsonBuilder().setVersion(1.2).create()之后

public class User {
   private String firstName;
   private String lastName;

   @Until(1.1) 
   private String emailAddress;

   @Until(1.1) 
   private String password;

 }

这时使用Gson解析的结果将会排除emailAddresspassword字段。

通常我们在解析Json的时候,会使用两种方式。一种是默认的fromJson另一种是实现TypeAdapter

先贴一个简单的Json文件,然后使用默认的解析方式来完成:

这里写图片描述

先定义JavaBean:

public class Message {
    @SerializedName("id")
    private long _id;

    @SerializedName("text")
    private String text;

    @SerializedName("geo")
    private List<Double> doubles =new ArrayList<>();

    @SerializedName("user")
    private User user;
}

然后直接解析

Gson gson =new Gson();
Message msg = gson.fromJson(Json,Message.class);

下面这个Json稍微比上一个复杂一点。这次我们使用TypeAdapter。

这里写图片描述

public class ParseJsonAdapter2 extends TypeAdapter<List<Message>>{
    private List<Message> readMessagesArray(JsonReader jsonReader) throws IOException {
        List<Message> messages = new ArrayList<>();
        jsonReader.beginArray();
        while (jsonReader.hasNext()) {
            messages.add(readMessage(jsonReader));
        }
        jsonReader.endArray();
        return messages;
    }

    private Message readMessage(JsonReader jsonReader) throws IOException {
        long id = -1;
        String text = null;
        User user = null;
        List<Double>geo =null;
        jsonReader.beginObject();
        while(jsonReader.hasNext()){
            String name = jsonReader.nextName();
            if(name.equals("id")){
                id = jsonReader.nextLong();
            }else if (name.equals("text")){
                text = jsonReader.nextString();
            }else if (name.equals("geo") && jsonReader.peek() != JsonToken.NULL) {
                 geo = readDoublesArray(jsonReader);
            }else if (name.equals("user")) {
                 user = readUser(jsonReader);
            }else{
                jsonReader.skipValue();
            }
        }
        jsonReader.endObject();
        return new Message(id, text, user, geo);
    }

    private User readUser(JsonReader jsonReader) throws IOException{
        String username = null;
        int followersCount = -1;
        jsonReader.beginObject();
         while (jsonReader.hasNext()) {
           String name = jsonReader.nextName();
           if (name.equals("name")) {
             username = jsonReader.nextString();
           } else if (name.equals("followers_count")) {
             followersCount = jsonReader.nextInt();
           } else {
               jsonReader.skipValue();
           }
         }
         jsonReader.endObject();
         return new User(username, followersCount);



    }

    private List<Double> readDoublesArray(JsonReader jsonReader) throws IOException{
         List<Double> doubles = new ArrayList<Double>();

         jsonReader.beginArray();
         while (jsonReader.hasNext()) {
           doubles.add(jsonReader.nextDouble());
         }
         jsonReader.endArray();
         return doubles;
    }

    @Override
    public List<Message> read(JsonReader arg0) throws IOException {
        return readMessagesArray(arg0);
    }

    @Override
    public void write(JsonWriter arg0, List<Message> arg1) throws IOException {
        // TODO Auto-generated method stub

    }
}

创建Gson,直接解析:

final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Message.class, new ParseJsonAdapter2());

final Gson gson = gsonBuilder.create();

Type type = new TypeToken<Message>(){}.getType();
List<Message>messages = gson.fromJson(Json_B,type);

使用TypeAdapter是使用流的方式来进行序列或反序列化的,所以是很高效的一种解析方式。那么解析流程是怎么样的呢:

  1. 首先根据你的Json 文本的结构创建方法。
  2. 调用 beginArray () 以读取数组的左方括号。然后创建一个 while 循环, 当 hasNext () 为 false 时终止。最后, 通过调用 endArray () 来读取数组的右方括号。
  3. 在对象处理方法中, 首先调用 beginObject () 读取对象的左大括号。然后创建一个 while 循环, 并根据其名称为局部变量赋值。当 hasNext () 为 false 时, 此循环应终止。最后, 通过调用 endObject () 来读取对象的右大括号。
  4. 当遇到嵌套对象或数组时, 委托给相应的方法去处理。
  5. 如果值可能为 null, 则应首先使用 peek () 进行检查。也可以使用 nextNull ()skipValue () 来赋值Null。

看一下,fromJson与registerTypeAdapter方法在入口处做了什么:

//fromJson
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
    Object object = fromJson(json, (Type) classOfT);
    return Primitives.wrap(classOfT).cast(object);
  }

//registerTypeAdapter
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
//...省略代码
 if (typeAdapter instanceof TypeAdapter<?>) {
      factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
    }
    return this;
}

首先我们注册了TypeAdapter以后,要注意后面两个参数,不要和使用fromJson传入的类型冲突了,保证type类型一致,返回的结果一致。

TypeToken:它是Gson提供的数据类型转换器,可以支持各种数据集合类型转换。

关于JsonSerializer

如果你不满意Gson默认的序列化,你可以自己实现这个接口,去做序列化的内容。
最后记得注册 GsonBuilder.registerTypeAdapter(Type, Object)

如果需要了解一个库怎么用,最好的办法不是百度,而是去看GitHub上面的相关文档


我看到有几个工程师每次改二行代码就提交一次,查看提交记录,里面一望无际的都是他的名字。这样看着觉得工作好TM认真啊!哎,谁让人家是Android Engineer 呢!?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值