场景一:存在一个对象User的json字符串,json字符串中存在对象类型为Integer或Double的属性值为空字符串,使用Gson将字符串转换为对象时报错,报错信息如下:
Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: empty String
at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:241)
at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:231)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172)
at com.google.gson.Gson.fromJson(Gson.java:803)
at com.google.gson.Gson.fromJson(Gson.java:768)
at com.google.gson.Gson.fromJson(Gson.java:717)
at com.google.gson.Gson.fromJson(Gson.java:689)
at JsonMain.main(JsonMain.java:15)
Caused by: java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at com.google.gson.stream.JsonReader.nextInt(JsonReader.java:1178)
at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:239)
... 8 more
对象信息如下:
package com.gj.bean;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 6941951312521096768L;
/** 用户id */
private Integer id;
/** 用户名 */
private String name;
/** 年龄 */
private Integer age;
/** 体重 */
private Double weight;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
}
使用代码如下:
import com.gj.bean.User;
import com.google.gson.Gson;
public class JsonMain {
public static void main(String[] args) {
String json = "{\"id\":1,\"name\":\"test1\",\"age\":\"\"}";
User obj = new Gson().fromJson(json, User.class);
}
}
原因:Gson转换时将空字符串设置到Integer或Double类型的属性上,导致类型不匹配报错
解决办法:针对不同的数值类型的场景,写对应场景的类型转换器,实现Gson的序列化和反序列化接口:JsonSerializer接口和JsonDeserializer,在反序列化接口方法中,判断json值是否为空字符串值,如果是,则返回null或对应类型的默认值
如下所示:
DoubleDefaultNullAdapter.java
package com.gj.config;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonSyntaxException;
/**
* @Description 将json字符串中Double类型字段值为空字符串的转化为null
* @Author gj
* @Date 2019/7/15
* @Version 1.0
**/
public class DoubleDefaultNullAdapter implements JsonSerializer<Double>, JsonDeserializer<Double> {
/**
* Gson invokes this call-back method during deserialization when it encounters
* a field of the specified type.
* <p>
* In the implementation of this call-back method, you should consider invoking
* {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to
* create objects for any non-trivial field of the returned object. However, you
* should never invoke it on the the same type passing {@code json} since that
* will cause an infinite loop (Gson will call your call-back method again).
*
* @param json
* The Json data being deserialized
* @param typeOfT
* The type of the Object to deserialize to
* @param context
* @return a deserialized object of the specified type typeOfT which is a
* subclass of {@code T}
* @throws JsonParseException
* if json is not in the expected format of {@code typeofT}
*/
public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
try {
if (json.getAsString().equals("")) {
// 定义为double类型,如果后台返回"",则返回null
return null;
}
} catch (Exception ignore) {
}
try {
return json.getAsDouble();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
/**
* Gson invokes this call-back method during serialization when it encounters a
* field of the specified type.
*
* <p>
* In the implementation of this call-back method, you should consider invoking
* {@link JsonSerializationContext#serialize(Object, Type)} method to create
* JsonElements for any non-trivial field of the {@code src} object. However,
* you should never invoke it on the {@code src} object itself since that will
* cause an infinite loop (Gson will call your call-back method again).
* </p>
*
* @param src
* the object that needs to be converted to Json.
* @param typeOfSrc
* the actual type (fully genericized version) of the source object.
* @param context
* @return a JsonElement corresponding to the specified object.
*/
public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
}
IntegerDefaultNullAdapter.java
package com.gj.config;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonSyntaxException;
/**
* @Description 将json字符串中Integer类型字段值为空字符串的转化为null
* @Author gj
* @Date 2019/7/15
* @Version 1.0
**/
public class IntegerDefaultNullAdapter implements JsonSerializer<Integer>, JsonDeserializer<Integer> {
/**
* Gson invokes this call-back method during deserialization when it encounters
* a field of the specified type.
* <p>
* In the implementation of this call-back method, you should consider invoking
* {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to
* create objects for any non-trivial field of the returned object. However, you
* should never invoke it on the the same type passing {@code json} since that
* will cause an infinite loop (Gson will call your call-back method again).
*
* @param json
* The Json data being deserialized
* @param typeOfT
* The type of the Object to deserialize to
* @param context
* @return a deserialized object of the specified type typeOfT which is a
* subclass of {@code T}
* @throws JsonParseException
* if json is not in the expected format of {@code typeofT}
*/
public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
try {
if (json.getAsString().equals("")) {
// 定义为Integer类型,如果后台返回"",则返回null
return null;
}
} catch (Exception ignore) {
}
try {
return json.getAsInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
/**
* Gson invokes this call-back method during serialization when it encounters a
* field of the specified type.
*
* <p>
* In the implementation of this call-back method, you should consider invoking
* {@link JsonSerializationContext#serialize(Object, Type)} method to create
* JsonElements for any non-trivial field of the {@code src} object. However,
* you should never invoke it on the {@code src} object itself since that will
* cause an infinite loop (Gson will call your call-back method again).
* </p>
*
* @param src
* the object that needs to be converted to Json.
* @param typeOfSrc
* the actual type (fully genericized version) of the source object.
* @param context
* @return a JsonElement corresponding to the specified object.
*/
public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src);
}
}
使用方式:
import com.gj.bean.User;
import com.gj.config.DoubleDefaultNullAdapter;
import com.gj.config.IntegerDefaultNullAdapter;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JsonMain {
public static void main(String[] args) {
String json = "{\"id\":1,\"name\":\"test1\",\"age\":\"\"}";
// User obj = new Gson().fromJson(json, User.class);
Gson gson = new GsonBuilder().registerTypeAdapter(Integer.class, new IntegerDefaultNullAdapter())
.registerTypeAdapter(Double.class, new DoubleDefaultNullAdapter()).create();
User obj = gson.fromJson(json, User.class);
System.out.println(gson.toJson(obj));
}
}
结果:
场景二:如果对象中含有日期或时间等属性值,最好将类型定义为String,如果定义为Date类型,那么它在json字符串中可能存在值为空字符串,反序列化时,会转换失败,如下图所示:
版权声明:本文为gj716原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。