使用Gson将对象json字符串转化成对象遇到的问题

  • Post author:
  • Post category:其他


场景一:存在一个对象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 版权协议,转载请附上原文出处链接和本声明。