【问题标题】:Is it possible to allow Gson to just skip fields with wrong types?是否可以允许 Gson 跳过类型错误的字段?
【发布时间】:2016-10-26 10:25:04
【问题描述】:

我有一个来自 SDK 的类,我无权更改,但我想将 JSON 有效的字符串序列化为。

但是,外部 API 有时会为日期字段输入错误的类型。

长话短说:我可以忽略 GSON 中的错误,还是告诉 Gson 忽略字段上的错误,只获取部分对象?

例如,字段应该是双精度,但我得到的是 Date(number)。不过反正我没用过,所以不在乎,也不需要整个过程都失败。我只想要 parcable 字段,错误字段为空。

注意:编写一个反序列化器来创建我想要由 Gson 创建的对象违背了我提出的目的。

这i行代码失败,因为单个字段错误:

Customer customer = gson.fromJson(settings.getCustomerObjectJSONString(), Customer.class);

我希望它跳过它无法解析的字段,因为我无权访问 Customer 类,因为它来自生成的 SDK/库。

【问题讨论】:

  • 怎么看错类型了?你能放一些示例数据吗?也将是巨大的预期和取得的成果
  • 我附上了错误的类型和预期的结果。但这个问题是我想要一个通用的解决方案。
  • 您可以编写您的自定义JsonDeserializer,您可以在其中实现更复杂的解析规则。
  • 看起来怎么样?
  • 请添加一些代码,看看你是如何反序列化的

标签: java json gson android


【解决方案1】:

我知道两种选择。

您可以使用 JSON 反序列化器 实现自行解析 JSON 元素。但是,对于传递给单个 gson 实例的任何 DTO,以下示例将影响所有 doubleDouble 字段,并且这种行为可能是不可取的。不幸的是,我不知道是否可以以“上下文”方式使用JsonDeserializer:例如如果它们是某个父类的字段,则让它适用于所有 doubleDouble 字段。

private static final class Dto {

    private double primitive;
    private Double nullable;
    private String string;

}

private static final class FailSafeDoubleJsonDeserializer
        implements JsonDeserializer<Double> {

    @Override
    public Double deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        if ( !element.isJsonPrimitive() ) {
            return null;
        }
        try {
            final JsonPrimitive primitive = (JsonPrimitive) element;
            final Number number = primitive.getAsNumber();
            return number.doubleValue();
        } catch ( final NumberFormatException ignored ) {
            return null;
        }
    }

}

private static final JsonDeserializer<Double> failSafeDoubleJsonDeserializer = new FailSafeDoubleJsonDeserializer();

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(double.class, failSafeDoubleJsonDeserializer)
        .registerTypeAdapter(Double.class, failSafeDoubleJsonDeserializer)
        .create();

public static void main(final String... args) {
    dump(gson.fromJson("{\"primitive\": 23, \"nullable\": 42, \"string\": \"foo bar\"}", Dto.class));
    dump(gson.fromJson("{\"primitive\": \"whatever\", \"nullable\": \"whatever\", \"string\": \"foo bar\"}", Dto.class));
}

private static void dump(final Dto dto) {
    out.println(dto.primitive + " " + dto.nullable + " " + dto.string);
}

另一个更底层的选项可以是类型适配器实现。与上一个示例相比,此示例的一个优点是您可以在已知可能损坏的 DTO 类中使用 @JsonAdapter 注释来注释失败的字段。

private static final class Dto {

    @JsonAdapter(FailSafeDoubleTypeAdapter.class)
    private double primitive;

    @JsonAdapter(FailSafeDoubleTypeAdapter.class)
    private Double nullable;

    private String string;

}

private static final class FailSafeDoubleTypeAdapter
        extends TypeAdapter<Double> {

    @Override
    public void write(final JsonWriter writer, final Double value) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Double read(final JsonReader reader)
            throws IOException {
        final JsonToken peek = reader.peek();
        if ( peek != NUMBER ) {
            reader.skipValue();
            return null;
        }
        return reader.nextDouble();
    }

}

private static final Gson gson = new Gson();

public static void main(final String... args) {
    dump(gson.fromJson("{\"primitive\": 23, \"nullable\": 42, \"string\": \"foo bar\"}", Dto.class));
    dump(gson.fromJson("{\"primitive\": \"whatever\", \"nullable\": {\"subValue\": \"whatever\"}, \"string\": \"foo bar\"}", Dto.class));
}

private static void dump(final Dto dto) {
    out.println(dto.primitive + " " + dto.nullable + " " + dto.string);
}

因此,两个示例都会生成以下输出:

23.0 42.0 富吧

0.0 空 foo 栏

  • {"primitive": 23, "nullable": 42, "string": "foo bar"}
  • {"primitive": "whatever", "nullable": {"subValue": "whatever"}, "string": "foo bar"}

有效载荷。

【讨论】:

  • 我来分析答案!谢谢
  • @AndreasRudolph 我试图以更强大的方式重新实现它,但我无法让它变得更好。如果您无法更改生成的代码,您可能希望使用 DTO 使您的类型膨胀,而您的 DTO 可能对解析错误友好(例如,您的 DTO 可能有一个可以转换为任何内容的 Object 字段)-不是很好的方法;或使用具有不同反序列化策略的单独 Gson 实例(例如,只需为您的“坏”类覆盖 double 字段反序列化) - 不幸的是,这也是一个不好的建议。或者更深入地了解ReflectiveTypeAdapterFactory
【解决方案2】:

我从另一个角度看待问题,即 OP 中提到的主要要求是

1) 您不在乎特定字段中存在什么值

2) 你不会使用特定的字段值,并且不希望反序列化器因为无效数据而失败

在上述情况下,您可以将特定字段标记为 TRANSIENT 或 STATIC。默认情况下,Gson 会排除所有标记为瞬态或静态的字段。

示例:-

private transient Date joiningDate;

【讨论】:

  • 正如我所说,我无法轻松访问该字段,因为它来自生成的库/SDK。不过谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-23
  • 2020-04-26
  • 1970-01-01
  • 2012-07-01
  • 1970-01-01
  • 2011-03-22
相关资源
最近更新 更多