程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> GSON完成Java對象與JSON格局對象互相轉換的完整教程

GSON完成Java對象與JSON格局對象互相轉換的完整教程

編輯:關於JAVA

GSON完成Java對象與JSON格局對象互相轉換的完整教程。本站提示廣大學習愛好者:(GSON完成Java對象與JSON格局對象互相轉換的完整教程)文章只能為提供參考,不一定能成為您想要的結果。以下是GSON完成Java對象與JSON格局對象互相轉換的完整教程正文


Gson是一個Java庫,用來完成Json和Java對象之間的互相轉換。Gson是一個托管在https://github.com/谷歌/gson的開源項目。

Gson中重要的類是Gson,也能夠應用類GsonBuilder在創立Gson對象的同時設置一些選項。
Gson對象在處置Json時不會保留任何狀況,所以應用者可以或許很輕松的對統一個Gson對象停止屢次序列化、反序列化等操作。

示例:根本應用

//Serialization
Gson gson = new Gson();
gson.toJson(1);   //==> prints 1
gson.toJson("abcd");  //==> prints "abcd"
gson.toJson(new Long(10)); //==> prints 10
int[] values = { 1 };
gson.toJson(values);  //==> prints [1]

//Deserialization
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean f = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String anotherStr = gson.fromJson("[\"abc\"]", String.class);

//Serialization
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj); 
//==> json is {"value1":1,"value2":"abc"}

示例:對象與Json之間轉換

界說BagOfPrimitives類:

class BagOfPrimitives {
 private int value1 = 1;
 private String value2 = "abc";
 private transient int value3 = 3;
 BagOfPrimitives() {
 // no-args constructor
 }
}

序列化為Json:

//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

處置對象時的一些細節:

  • 推舉應用公有字段(譯者:可以經由過程反射獲得公有字段的稱號和值)
  • 沒有需要應用標注指明哪些字段該被序列化或許反序列化。在以後類(也包含其父類)中的一切字段都默許會被序列化/反序列化。
  • 假如某字段在聲明時應用了症結字transient,默許情形下不會被序列化/反序列化。
  • Gson以下處置null字段:
  • 序列化時刻null字段會被跳過
  • 反序列化時,類中有但Json中沒有的字段將設值為null。
  • synthetic字段不會被序列化/反序列化。
  • 在內部類(outer classes)中的外部類(inner classes)、匿名類(anonymous classes)和部分類(local classes)中的字段不會被序列化/反序列化。

嵌套類(包含外部類)的處置

Gson可以很輕松地序列化嵌套類,且可以或許反序列化靜態的嵌套類。Gson沒法主動地反序列化純潔的外部類,是由於外部類的無參結構函數須要援用包括它的對象(即內部類的實例)。要反序列化靜態類,可以將外部類靜態化或許供給一個自界說的實例發明器(instance creator)。上面是一個示例:

public class A {
 public String a;

 class B {

 public String b;

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

下面的類B沒法被Gson序列化。因為類B是一個(非靜態的)外部類,Gson也沒法反序列化{"b":"abc"}到類B的實例中。假如B被聲明為static class B,那末Gson就可以對這個字符串反序列化了。

別的一個處理辦法是為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();
 }
}

這類辦法是可行的,然則不推舉。(譯者表現沒看懂這個實例創立器,不曉得該怎樣用)

示例:數組

Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};

//Serialization
gson.toJson(ints);  ==> prints [1,2,3,4,5]
gson.toJson(strings); ==> prints ["abc", "def", "ghi"]

//Deserialization
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);
==> ints2 will be same as ints

Gson也支撐具有龐雜數據類型的多維數組。

示例:聚集(Collection)

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

處置聚集(Collection)時的限制:

  • 可以序列化隨意率性對象的聚集,反序列化就不可了。
  • 反序列化時,聚集必需是指定的泛型。

序列化/反序列化泛型

當應用toJson(obj)時,Gson挪用obj.getClass()獲得字段信息以在序列化中應用。相似的,也能夠將對象MyClass.class作為參數傳遞給fromJson(json, MyClass.class)辦法,這可以在在對象不是泛型的時刻應用。不外,當對象是一個泛型類型的對象,因為Java中類型擦除(Type Erasure)這一機制,泛型類型信息會喪失。上面的例子將解釋這一點:

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

下面的代碼將value說明為Bar類型,這是由於Gson挪用foo.getClass()獲得類的信息,然則這類誰人辦法前往的是一個原始的類,即Foo.class。這意味著Gson沒法曉得這是一個Foo<Bar>類型的對象。

要處理這個成績,可所以為你的泛型指定准確的參數化的類型。可使用TypeToken類做到:

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

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

經由過程Gson,你不須要做任何特別的工作便可以序列化聚集:toJson(collection)會輸入使人滿足的成果。

但是,經由過程fromJson(json, Collection.class)反序列化是不可的,這是由於Gson沒法將json中的的內容與類型對應起來。Gson須要你在fromJson中供給一個通用版本的聚集類型。你有三個選擇:

計劃1:應用Gson解析器的API(初級的流解析器或許DOM解析器JsonParser)去解析數組元素,然後應用Gson.fromJson()處置每個數組元素。這是首選的計劃。
計劃2:為Collection.class注冊一類型適配器將數組中的元素映照到適合的對象。這類辦法的缺陷是會使你在處置其他的聚集類型時刻發生未便。
計劃3:為MyCollectionMemberType注冊一個類型適配器,在fromJson中應用Collection<MyCollectionMemberType>。只要當數組看起來像一個高等的元素或許你可以或許將字段類型改成Collection<MyCollectionMemberType>,這類辦法才比擬可行。
內置的序列化/反序列化器

Gson為經常使用的然則默許表現能夠其實不適合的類供給了序列化/反序列化器。
上面是這些類的一個列表:

  • java.net.URL,例如會序列化為字符串 http://code.谷歌.com/p/谷歌-gson/
  • java.net.URI,例如會序列化為字符串/p/谷歌-gson/

自界說序列化/反序列化

有時刻,Gson的默許完成其實不是你想要的,這在處置一些類庫時(例如DateTime)時比擬罕見。

Gson許可你注冊自界說的序列化/反序列化器。要如許做的話,你須要完成以下幾個部門:

Json序列化器:須要為一個對象自界說序列化
Json反序列化器:須要為一個類型自界說反序列化
類創立器:假如存在無參結構函數或許曾經注冊了一個反序列化器,就不須要了。

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

registerTypeAdapter會檢討類型適配器能否完成了多個接口,並為這些接口注冊類型適配器。

寫一個序列化器

上面是一個為DateTime自界說序列化器的示例:

private class DateTimeSerializer implements JsonSerializer<DateTime> {
 public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
 return new JsonPrimitive(src.toString());
 }
}

Gson在序列化DateTime實例時會挪用toJson()。

寫一個反序列化器

上面的示例是講若何寫一個DateTime類的反序列化器:

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

當Gson須要將一個JSON字符串發反序列化為DateTime對象時,會挪用 fromJson()。

關於序列化器/反序列化器,應留意:

  • 許多情形下,你想要注冊一個處置法式將泛型與一個原始類型對應起來,
  • 例如,假設你有一個叫做Id的類來表現和轉換Id
  • Id<T>類型中,一切的泛型具有雷同的序列化器,這個序列化器就是輸入id的值
  • 反序列化器很像,但其實不會完整一樣。例如要前往一個Id<T>對象,須要挪用new Id(Class<T>, String)。
  • Gson支撐注冊一個處置法式,你也能夠為指定的泛型注冊指定的處置法式。toJson和fromJson的Type參數包括了泛型信息以贊助你寫出一個可以將一切的泛型與統一個原始類型對應起來的處置法式。

寫一個實例創立器

在反序列化一個對象時,Gson須要創立一個類的實例。在序列化/反序列化時具有優越表示的類是指這個類具有一個無參結構函數。平日,當處置一個類庫中沒有沒有參結構函數的類時,須要應用實例創立器。

實例創立器示例:

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

不外,有時你須要基於真實的參數化類型來創立實例。在這類情形下,你可以將類型參數傳遞給createInstance辦法。上面是一個例子:

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

在下面的示例中,假如未將真實的類型傳遞給參數化類型,Id類的實例是沒法創立的。經由過程給辦法傳遞參數type,我們才得以處理這個成績。這裡,type對象可以看作是Id<Foo>的Java參數化類型的表現,響應的實例應當被綁定到Id<Foo>。因為類Id只要一個參數化類型的參數T,我們應用getActualTypeArgument()前往的類型數組的第0個元素,在這個例子中就是Foo.class。

緊湊的輸入 VS 幽美的輸入

Gson中Json默許的輸入是緊湊的JSON格局。也就是說在JSON中沒有過剩的空白符。所以在JSON的輸入中字段名和字段值之間、字段之間、數組元素之間是沒有空白的。別的,null字段不會被輸入(留意:在聚集和數組對象中null會被保存的)。

假如要輸入的幽美些,你須要應用GsonBuilder對Gson的實例停止設置裝備擺設。JsonFormatter不存在於私有API中,所以客戶端沒法設置裝備擺設默許的輸入設置。如今我們只供給了JsonPrintFormatter,其默許情形下每行80個字符,縮進應用2個字符,左邊距是4個字符。

上面的示例展現了若何讓Gson實例應用JsonPrintFormatter,而不是應用默許的JsonCompactFormatter。

Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonOutput = gson.toJson(someObject);

空對象

在Gson的默許完成中,null對象是被疏忽的。這可讓輸入格局(既可以以為是序列化的成果)加倍慎密;不外客戶端必需為其界說一個默許的值,以使得JSON可以或許正常的反序列化。

假如要讓Gson實例可以序列化null,可以:

Gson gson = new GsonBuilder().serializeNulls().create();

留意,當序列化null的時,會在JsonElement構造中添加一個JsonNull元素。是以,我們可以可以在自界說的序列化器/反序列化器中應用這個對象(gson)。

上面是一個例子:

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標注來保護統一個對象的多個版本。這個標注可以用在類和字段上,未來也會支撐用在辦法上。為了應用這個特征,你須要設置裝備擺設Gson實例,讓其疏忽年夜於某個版本號的字段和對象。假如沒有在Gson對象中設置版本,序列化/反序列化時會應用一切的字段和類。

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

從序列化/反序列化中消除字段

Gson支撐應用許多辦法往來來往除類、字段、字段類型。假如上面的辦法沒法知足你的需求,可使用自界說序列化/反序列化器的辦法。

1.Java Modifier Exclusion

默許情形下,假如將一個字段聲明為transient,這個字段就會被消除。別的,假如一個字段被聲明為static,默許情形下這個字段也會被消除。假如要包括某些聲明為transient的字段,你可以如許做:

import java.lang.reflect.Modifier;

Gson gson = new GsonBuilder()
 .excludeFieldsWithModifiers(Modifier.STATIC)
 .create();

留意,在excludeFieldsWithModifiers辦法中,你可使用隨意率性數目的Modifier常量。例如:

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

2.應用@Expose字段消除

這個特征許可你在類中標志特定的字段使其在序列化/反序列化中不被消除/被消除。要應用這個標注,你應當應用new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()創立Gson。Gson實例會消除類中一切沒被@Expose標注的字段。

3.用戶界說消除戰略

假如下面的消除辦法沒法知足需求,你也能夠自界說本身的消除戰略。更多內容,可以參考ExclusionStrategy JavaDoc。

上面的例子展現了若何消除應用了@Foo標注的字段,消除String類的頂級類型或許聲明的字段類型:

 

@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字段定名的支撐

Gson的一些預界說的字段定名戰略,可以將尺度的Java字段稱號(也就是駝峰定名法,例如sampleFieldNameInJava)轉換成一個Json的字段名(也就是sample_field_name_in_java或許SampleFieldNameInJava)。更多信息,可以參考FieldNamingPolicy。

Gson也有一個基於標注的戰略讓客戶端自界說字段的稱號。這個戰略下,假如供給了一個不法的字段名作為標注的值,會使Gson拋出Runtime異常。

上面的示例展現了若何應用這兩種Gson定名戰略:

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

假如要自界說稱號,可使用@SerializedName標注。

在序列化器和反序列化器之間同享狀況

有時你會須要在序列化器和反序列化器之間同享狀況,你可使用上面的三個辦法到達目標:

  • 在一個靜態字段中存儲同享狀況
  • 將序列化/反序列化器聲明為一個父類型的外部類,然後應用父類型的實例的字段存儲同享狀況
  • 應用Java中的ThreadLocal

前兩種辦法不是線程平安的,第三種是。


GSON解析null失足處理方法
GSON有一個缺陷就是沒法設置null調換,
我們只妙手動的批量調換辦事器前往的null了,正常的接口界說的時刻是相對不許可辦事器前往null的,後台成果卻總會湧現null!
假如搜刮的話有一個罕見的謎底,

Gson gson = new GsonBuilder().serializeNulls().create();

然則這個卻沒法處理反序列成績,怎樣處理呢?
處理方法以下:

Gson gson = new GsonBuilder().registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory()).create();
//然後用下面一行寫的gson來序列化和反序列化實體類type
gson.fromJson(json, type);
gson.toJson(type);
//NullStringToEmptyAdapterFactory的代碼

public class NullStringToEmptyAdapterFactory<T> implements TypeAdapterFactory {
 @SuppressWarnings("unchecked")
 public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
  Class<T> rawType = (Class<T>) type.getRawType();
  if (rawType != String.class) {
   return null;
  }
  return (TypeAdapter<T>) new StringNullAdapter();
 }
}
// StringNullAdapter代碼

public class StringNullAdapter extends TypeAdapter<String> {
 @Override
 public String read(JsonReader reader) throws IOException {
  // TODO Auto-generated method stub
  if (reader.peek() == JsonToken.NULL) {
   reader.nextNull();
   return "";
  }
  return reader.nextString();
 }
 @Override
 public void write(JsonWriter writer, String value) throws IOException {
  // TODO Auto-generated method stub
  if (value == null) {
   writer.nullValue();
   return;
  }
  writer.value(value);
 }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved