当前位置: 首页 > 知识库问答 >
问题:

在Gson自定义序列化中为双字段分配null

钱宇
2023-03-14

我试图使用Gson自定义序列化将以下类序列化为Json。

public class Product{
   public String prodId;
   public Double prodPrice;
}
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;
}

}

共有1个答案

暴骏奇
2023-03-14

您可以有一个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
产品坏,价格为空

您的序列化程序存在一些设计问题。最重要的是,如果发生异常,代码会吞下它,很抱歉,当我看到双retVal=null时,我错过了它;返回新的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

根本原因是:class classOfErimity=target.getClass();在GSON中。Gson有一个特殊的JSONnull元素: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;
}

注意,虽然使用了Java8Lambda,但您可以轻松地将其转换为匿名类。还要注意,您可以有一个Gson实例。这可能就是你所需要的,因为Gson本身可以处理null,等等。

 类似资料:
  • 我想序列化特定字段或类的空值。 在GSON中,选项适用于整个JSON。 例子: 使用GSON创建JSON: 电流输出: 期望输出: 如何达到预期的输出? 首选解决方案应具有以下特性: 默认情况下不要序列化空值 为具有特定注释的字段序列化空值。

  • 首先,我想说这是一个大学项目。 我有三节课<代码>订单抽象类和从订单继承的和类。 我使用Gson序列化/反序列化子类,但我遇到了一点问题。订单类有一个字段,gson使用该字段来确定订单类型,DineIn或Delivery。 序列化工作得很好。问题是,每当我尝试反序列化时,类型字段值都不会读取,并且总是设置为null,即使它存在于JSON文件中。当有很多字段时,就会发生这种情况,因为当我尝试在较小的

  • 问题内容: 确定,所以我编辑了问题,因为它不够清楚。 编辑2 :更新了JSON文件。 我在Android应用程序中使用GSON,我需要解析来自服务器的JSON文件,这些文件有点太复杂了。我不想让我的对象结构太沉重,所以我想简化内容: 所以我的对象的结构将不是JSON文件的结构。 例如,如果在JSON中,我有以下内容: 我不想保留我当前的对象结构,即一个对象,其中包含一个和一个“总计”。但是我只想将

  • 我有一个JestClient(elasticsearch)响应,我试图将其反序列化为一个对象。该对象包含两个DateTime字段,而在响应中,它们是字符串,因此我得到: 所以,我创建了一个自定义反序列化器来解决这个问题…然而,无论我做什么,我总是得到同样的错误。不知何故它没有注册使用它? 最后,我试图解析JestClient响应的代码: 无论我尝试什么,我总是得到上面的错误,我甚至不确定在这一点上

  • 我收到的是API长json,例如: 我想用Gson反序列化它。一切正常,但字段的“SOME_FIELD”值总是带有烦人的空格。我想修剪()这个字段值(API不能更改)。我知道我可以使用JsonDeserializer,但我必须手动读取所有字段。是否可以在反序列化时只编辑一个感兴趣的字段,但对其余字段使用自动反序列化?

  • 我有一个不能用Gson正确序列化的类(类只是name和HashMap),所以我编写了一个自定义序列化程序来从HashMap中打印名称和键值对。 此外,这是实际打印的内容,序列化整个对象并不像我所期望的那样工作,也不只是直接打印对象。 任何帮助都将不胜感激。