我正在尝试使用Gson自定义序列化将以下类序列化为Json。
public class Product{
public String prodId;
public Double prodPrice;
}
我想检查Double属性(prodPrice)的值是否为" -1.0"并且如果它是" -1.0"则为该字段分配空值。我已经编写了以下Gson自定义序列化程序来执行此操作。
public class GsonUtil {
public static String getJSONString(Object input) throws Exception {
if (input == null) {
throw new Exception("JSON Conversion failed. Input string is null");
}
try {
JsonSerializer<Double> dser = getDoubleSerializer();
Gson gson = new GsonBuilder().serializeNulls()
.registerTypeAdapter(Double.class, dser)
.create();
String jsonStr = gson.toJson(input);
if (jsonStr == null) {
throw new Exception("Json String is null");
}
return jsonStr;
} catch (Exception e) {
}
return null;
}
private static JsonSerializer<Double> getDoubleSerializer() {
JsonSerializer<Double> ser = new JsonSerializer<Double>() {
public JsonElement serialize(Double dval, Type arg1,
JsonSerializationContext arg2) {
if(!(dval.equals(-1.0))){
return new JsonPrimitive(dval);
}
else{
Double retVal = null;
return new JsonPrimitive(retVal);
}
}
};
return ser;
}
}
当我这样做时,自定义序列化程序不起作用,当我尝试分配除null之外的其他一些值时,相同的自定义序列化工作正常。
如果我做错了,请告诉我,如果可以通过其他方式实现,请告诉我。 谢谢。
答案 0 :(得分:0)
每次可能需要这样的反序列化时,您可以让一个Gson
实例不创建另一个实例。 Gson支持可以绑定到特定对象字段并使用@JsonAdapter
注释的特殊类型适配器。请注意,它不会影响相同类型的其他对象,除非使用此批注进行批注,因此保留原始Gson配置。
因此Product.java
可以拥有prodPrice
字段的注释:
final class Product {
@SerializedName("prodId")
String id;
@JsonAdapter(SpecialDoubleTypeAdapter.class)
@SerializedName("prodPrice")
Double price;
}
现在只需创建一个符合您要求的特殊类型适配器:
final class SpecialDoubleTypeAdapter
extends TypeAdapter<Double> {
private static final TypeAdapter<Double> specialDoubleTypeAdapter = new SpecialDoubleTypeAdapter();
// Gson can instantiate it itself even with a private constructor
private SpecialDoubleTypeAdapter() {
}
// In case if it's necessary to instantiate it manually
static TypeAdapter<Double> getSpecialDoubleTypeAdapter() {
return specialDoubleTypeAdapter;
}
@Override
@SuppressWarnings("resource")
public void write(final JsonWriter out, final Double value)
throws IOException {
// write the value as null if no value provided or it's considered "illegal" (however it's a good place to review and the right-hand expression)
if ( value != null || value < 0 ) {
out.value(value);
} else {
out.nullValue();
}
}
@Override
public Double read(final JsonReader in)
throws IOException {
final JsonToken token = in.peek();
switch ( token ) {
case NULL:
// read the null token, this is mandatory
in.nextNull();
return null;
case NUMBER:
// read the number token as double
final double d = in.nextDouble();
// assuming the negative price should be null, not just -1.0
return d >= 0 ? d : null;
// an opinion-based case group: you can use default
// but in my opinion explicit mapping is explicit
case BEGIN_ARRAY:
case END_ARRAY:
case BEGIN_OBJECT:
case END_OBJECT:
case NAME:
case STRING:
case BOOLEAN:
case END_DOCUMENT:
throw new MalformedJsonException("Unexpected token: " + token);
// so this would never happen unless Gson adds a new token some day...
default:
throw new AssertionError("must never happen");
}
}
}
然后做一点测试:
public static void main(final String... args) {
final Gson gson = new Gson();
test(gson, "{\"prodId\":\"good\",\"prodPrice\":100}");
test(gson, "{\"prodId\":\"bad\",\"prodPrice\":-1}");
}
private static void test(final Gson gson, final String json) {
final Product product = gson.fromJson(json, Product.class);
out.print("Product ");
out.print(product.id);
out.print(", price ");
out.println(product.price);
}
上面的演示将产生:
产品好,价格100.0
产品不好,价格无效
您的序列化程序存在一些设计问题。主要的一点是你的代码在发生时会吞下一个异常,而且很抱歉,当我看到Double retVal = null; return new JsonPrimitive(retVal);
时,我已经错过了它。请不要那样做 - 或者你的代码会为严肃的问题保持沉默。接下来,您可以重新抛出异常,这就是它:
java.lang.NullPointerException
at com.google.gson.JsonPrimitive.isPrimitiveOrString(JsonPrimitive.java:278)
at com.google.gson.JsonPrimitive.setValue(JsonPrimitive.java:101)
at com.google.gson.JsonPrimitive.<init>(JsonPrimitive.java:56)
at q42278197.m2.Q42278197$1.serialize(Q42278197.java:77)
at q42278197.m2.Q42278197$1.serialize(Q42278197.java:71)
at com.google.gson.internal.bind.TreeTypeAdapter.write(TreeTypeAdapter.java:81)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:125)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:243)
at com.google.gson.Gson.toJson(Gson.java:669)
at com.google.gson.Gson.toJson(Gson.java:648)
at com.google.gson.Gson.toJson(Gson.java:603)
at com.google.gson.Gson.toJson(Gson.java:583)
at q42278197.m2.Q42278197.getJSONString(Q42278197.java:58)
... 7 more
根本原因是:Gson中的Class<?> classOfPrimitive = target.getClass();
。 Gson有一个特殊的JSON null
元素:JsonNull
单例,只有一个实例INSTANCE
。因此,JsonSerializer
可能会将其归还为现有值:return JsonNull.INSTANCE;
这是一个经过重新设计的实施:
private static String toJsonBySpecialDoubles(final Object input)
throws NullPointerException {
if ( input == null ) {
throw new NullPointerException("input");
}
return specialDoubleGson.toJson(input);
}
private static final Gson specialDoubleGson = new GsonBuilder()
.serializeNulls()
.registerTypeAdapter(Double.class, getSpecialDoubleSerializer())
.create();
private static JsonSerializer<Double> getSpecialDoubleSerializer() {
return (value, type, context) -> !value.equals(-1.0) ? new JsonPrimitive(value) : JsonNull.INSTANCE;
}
请注意,使用了Java 8 lambda,但您可以轻松地将其转换为匿名类。另请注意,您可以拥有一个Gson实例。这可能就是你所需要的,因为Gson本身可以处理空值,等等。