码迷,mamicode.com
首页 > 其他好文 > 详细

GSON使用的学习笔记,进阶篇(三)

时间:2014-05-03 17:44:39      阅读:230      评论:0      收藏:0      [点我收藏+]

标签:gson   json   

        本篇笔记内容比较杂乱,没有专门去整理。

TypeAdapter

        现在轮到TypeAdapter类上场,但考虑到gson默认行为已足够强大,加上项目实践中应用json时场景不会太复杂,所以一般不需要自定义TypeAdapter。TypeAdapter优点是集成了JsonWriter和JsonReader两个类,定义了一套与gson框架交互的良好接口,同时便于管理编码和解码的实现代码,不至于太零碎。因而在了解JsonReader和JsonWriter的使用方法之后,自定义TypeAdapter类来完成特定类的编码和解码也就不困难了。


TypeToken

Type listType = new TypeToken<List<String>>() {}.getType();//!!!
List<String> target = new LinkedList<String>();
target.add("blah");

Gson gson = new Gson();
String json = gson.toJson(target, listType);
List<String> target2 = gson.fromJson(json, listType);// !!!

        上述代码在之前的样例里看到过,这里单独拿出来研究。行尾有!!!的两行是这里要特别关注的,原因是看起来很怪异。初次看到第一行代码的时候感觉非常怪异,很奇怪为什么需要设计这样的API。仔细思索之后,发现设计者真实的目的其实非常简单,只是为了提取泛型的类型,原因是Java的泛型对象的类型不能直接通过类型本身获取到,比如类似List<String>.class的代码是无法通过编译,原因和Java泛型的实现机制有关系。

        分析TypeToken类的实现代码,发现GSON的开发者想出了一个比较有意思的实现方法,既然不能直接通过类型本身得到真实的类型对象,那么就从对象本身得到。而TypeToken就是这一想法的通用实现。TypeToken类本身不能直接实例化,使用时需要定义其子类对象,这时即通过子类对象来获取其模板类型参数,也就得到了泛型类型的真实类型。

控制缩进

        比如面对类似 [{"Qualified Name":"Jackie","age":30,"contact":{"email":"email","phoneno":"phoneno"}},{"Qualified Name":"Jackie Boy","age":1,"contact":{"email":"email","phoneno":"phoneno"}}] 的json格式字符串,对于程序来说理解这样的字符串毫无压力,但对于人来说困难就比较大了。那怎么办呢?gson有办法,通过修改gson的默认行为,可以在输出成json字符串时,提供缩进使用字符串表现出良好的格式。样例代码和缩进后的输出如下。

final Gson gson = new GsonBuilder().setVersion(1.1).setPrettyPrinting().create();// 调用setPrettyPrinting方法,改变gson对象的默认行为
如下即是输出
[
  {
    "Qualified Name": "Jackie",
    "age": 30,
    "contact": {
      "email": "email",
      "phoneno": "phoneno"
    }
  },
  {
    "Qualified Name": "Jackie Boy",
    "age": 1,
    "contact": {
      "email": "email",
      "phoneno": "phoneno"
    }
  }
]

        这个特性很有意思,但项目实际开发的时候可能意义不大,毕竟换行、缩进都是占用空间的。日志里输出json格式的字符串时,倒是可以考虑写入调整过格式后的json字符串,在事后分析日志时方便阅读,给运维同事减轻负担。

输出空引用

        基于已有的使用经验,如果不做对字段进行特别的处理,对于引用类型的成员,如果取值为null时,编码后将不会出现在json字符串中,这是gson的默认行为。但通过修改gson的默认行为,可以将取值为null的字段也输出到json字符串中。样例代码如下,为了方便查看,还设置了缩进。

final Gson gson = new GsonBuilder().setVersion(1.1).serializeNulls().setPrettyPrinting().create();// 调用serializeNulls方法,改变gson对象的默认行为
如下是输出
[
  {
    "Qualified Name": "Jackie",
    "age": 30,
    "contact": {
      "email": "email",
      "phoneno": "phoneno"
    },
    "address": null,// address和location被原样输出
    "location": null
  },
  {
    "Qualified Name": "Jackie Boy",
    "age": 1,
    "contact": {
      "email": "email",
      "phoneno": "phoneno"
    },
    "address": null,
    "location": null
  }
]

// 如下是类定义
@Data
class Person {
    @Since(1.0)
    @SerializedName("Qualified Name")
    private String name;
    @Since(1.1)
    private int age;
    @Since(1.0)
    private Map<String, Object> contact;

    private String address;
    private String location;

    public Person() {
        contact = new HashMap<String, Object>();
    }

    public void putValue(final String key, final Object value) {
        contact.put(key, value);
    }
}


Gson判断Java Bean的字段是否需要格式化为json的方法

        这是一个好问题。有这么几种可能:

  1. 从Java Bean类的元数据中提取成员列表,然后根据各个成员的类型来生成对象的json表示;
  2. 可以提取符合getter/settter方法命名规范的公有方法列表,然后依据这个列表来生成对象的json表示; 

        设计如下的样例代码,可以检验一下刚才的猜测。

import java.util.HashMap;
import java.util.Map;

import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;

import com.google.gson.Gson;

public class JsonTest {
    public static void main(final String[] args) {
        final Gson gson = new Gson();

        final Person jack1 = new Person();
        jack1.setAge(30);
        jack1.setName("Jackie");

        jack1.putValue("email", "email");
        jack1.putValue("phoneno", "phoneno");

        final String json = gson.toJson(jack1);
        System.out.println(json);

        final Gson gson2 = new Gson();
        final Person p = gson2.fromJson(json, Person.class);

        System.out.println(p);
    }
}

@Data
class Person {
    private String name;
    @Getter(AccessLevel.NONE)
    // @Setter(AccessLevel.NONE)
    private int age;
    private Map<String, Object> contact;

    private String address;
    private String location;

    public Person() {
        age = 20;
        contact = new HashMap<String, Object>();
    }

    public void putValue(final String key, final Object value) {
        contact.put(key, value);
    }
    /*
    public int getAGe() {
        return age;
    }

    public int getAge() {
        return 90;
    }
    */
}
        样例代码的输出如下。

{"name":"Jackie","age":30,"contact":{"email":"email","phoneno":"phoneno"}}
Person(name=Jackie, age=30, contact={email=email, phoneno=phoneno}, address=null, location=null)

        前述代码主要是在验证成员的公有方法对gson为对象生成json字符串时的影响。比如通过使用注解,控制lombok不为Person类的成员age生成getter方法,或者写一个命名不合要求的访问方法,或者写一个命名符合要求,但是取值与成员age的值无关的公有getter方法。经验证,这几种修改方法对最终的结果没有任何影响,因而从上面的样例代码可以得出结论,gson在为Bean对象生成json时,并没有依赖类定义中的公有方法。假如gson直接使用Bean元数据中的成员信息来直接生成json的话,暂时没有想到怎样设计样例代码来检验,所以只好单步跟踪gson在生成json时的代码。最终跟踪到了如下的一段代码。

        // 如下代码来自于gson 2.2.4,版权归作者所有
        Field[] fields = raw.getDeclaredFields();
        for (Field field : fields) {
        boolean serialize = excludeField(field, true);
        boolean deserialize = excludeField(field, false);
        if (!serialize && !deserialize) {
          continue;
        }
        field.setAccessible(true);
        Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
        BoundField boundField = createBoundField(context, field, getFieldName(field),
            TypeToken.get(fieldType), serialize, deserialize);
        BoundField previous = result.put(boundField.name, boundField);
        if (previous != null) {
          throw new IllegalArgumentException(declaredType
              + " declares multiple JSON fields named " + previous.name);
        }
      }
        这段代码在类com.google.gson.internal.bind.ReflectiveTypeAdapterFactory的方法getBoundFields中,不相关的部分没有列出来。从这段代码可以看出gson直接读取JavaBean的成员信息来完成json的生成,并没有校验成员是否定义了get/set方法。

GSON使用的学习笔记,进阶篇(三),布布扣,bubuko.com

GSON使用的学习笔记,进阶篇(三)

标签:gson   json   

原文地址:http://blog.csdn.net/jackie_xiaonan/article/details/16853215

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!