当前位置:网站首页>使用Gson解析错误json数据
使用Gson解析错误json数据
2022-07-17 05:09:00 【秦汉春秋】
在拉取数据时,有时候会遇到一些错误数据导致整张表都无法正常存储的情况,比如这种错误:
com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: Invalid long: "null"
原json数据是这样的:
{
"id": "1202075683237523457",
"device_type": "00001001",
"device_id": "000d6f000a7ef40d",
"mode_id": "null",
"cmd_id": "color",
"cmd_order": null,
"cmd_papam": "{\"p1\":\"175\",\"p2\":\"240\",\"p3\":\"188\",\"p4\":\"\"}"
}
但需要序列化的mode_id本应该是一个long型,但由于某种原因,服务端提供的数据中出现了"null"这种无法正常转换为long的数据,那么gson在反序列化json数据的时候,就会出现异常。
这个时候,最好的方法自然是要求服务端校验数据…
当然在无奈的情况下,只能强行解析这样子。
用法
这个时候就需要用到gson的一些特殊用法:
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter)
顾名思义,用以注册TypeAdapter的方法,TypeAdapter是gson的核心模块之一,用于转换与解析数据,不可或缺。
在通常情况下,仅需要一行代码就能将json数据转换为想要的Model类型,比如想让将上面的json数据反序列化为一个ModeCmd实例:
Gson gson = new Gson();
ModeCmd modeCmd = gson.fromJson(json,new TypeToken<ModeCmd>(){
}.getType());
这种情况下,其实是使用的gson默认配置的TypeAdapter,遇到错误数据的时候,会按正常逻辑抛出异常,这个时候可以写一个JsonDeserializer通过registerTypeAdapter配置一个TypeAdapter,使其在解析ModeCmd数据时,使用JsonDeserializer来解析。
JsonDeserializer可以写成这样:
private JsonDeserializer<ModeCmd> modeCmdJsonDeserializer = new JsonDeserializer<ModeCmd>() {
@Override
public ModeCmd deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
json.getAsJsonObject().get("mode_id").getAsLong();
} catch (Exception ex) {
ex.printStackTrace();
json.getAsJsonObject().addProperty("mode_id", "0");
}
return gson.getGson().fromJson(json, typeOfT);
}
};
这里是将mode_id的数据强行换为“0”,也可以做其他处理。
最后使用
Gson gson = new GsonBuilder().registerTypeAdapter(ModeCmd.class, modeCmdJsonDeserializer).create();
ModeCmd modeCmd = gson.fromJson(json,new TypeToken<ModeCmd>(){
}.getType())
得到的gson去解析数据即可。
原理
之所以可以这么做的缘由,是因为gson解析数据也是遍历JsonTree一层一层选择合适TypeAdapter去解析当前层数据的,并且gson在新建时(构造方法)已经内置了大量TypeAdapter:
//Gson
List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
// built-in type adapters that cannot be overridden
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
factories.add(ObjectTypeAdapter.FACTORY);
// the excluder must precede all adapters that handle user-defined types
factories.add(excluder);
// user's type adapters
factories.addAll(typeAdapterFactories);
// type adapters for basic platform types
factories.add(TypeAdapters.STRING_FACTORY);
factories.add(TypeAdapters.INTEGER_FACTORY);
factories.add(TypeAdapters.BOOLEAN_FACTORY);
factories.add(TypeAdapters.BYTE_FACTORY);
factories.add(TypeAdapters.SHORT_FACTORY);
TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
factories.add(TypeAdapters.newFactory(double.class, Double.class,
doubleAdapter(serializeSpecialFloatingPointValues)));
factories.add(TypeAdapters.newFactory(float.class, Float.class,
floatAdapter(serializeSpecialFloatingPointValues)));
factories.add(TypeAdapters.NUMBER_FACTORY);
factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
factories.add(TypeAdapters.CHARACTER_FACTORY);
factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
factories.add(TypeAdapters.URL_FACTORY);
factories.add(TypeAdapters.URI_FACTORY);
factories.add(TypeAdapters.UUID_FACTORY);
factories.add(TypeAdapters.CURRENCY_FACTORY);
factories.add(TypeAdapters.LOCALE_FACTORY);
factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
factories.add(TypeAdapters.BIT_SET_FACTORY);
factories.add(DateTypeAdapter.FACTORY);
factories.add(TypeAdapters.CALENDAR_FACTORY);
factories.add(TimeTypeAdapter.FACTORY);
factories.add(SqlDateTypeAdapter.FACTORY);
factories.add(TypeAdapters.TIMESTAMP_FACTORY);
factories.add(ArrayTypeAdapter.FACTORY);
factories.add(TypeAdapters.CLASS_FACTORY);
// type adapters for composite and user-defined types
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
factories.add(jsonAdapterFactory);
factories.add(TypeAdapters.ENUM_FACTORY);
factories.add(new ReflectiveTypeAdapterFactory(
constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));
this.factories = Collections.unmodifiableList(factories);
包括了JsonElement/Integer/Long各种基础类型相关的TypeAdapter,比如解析字符串使用的是STRING_FACTORY :
public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);
public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
@Override
public StringBuilder read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return new StringBuilder(in.nextString());
}
@Override
public void write(JsonWriter out, StringBuilder value) throws IOException {
out.value(value == null ? null : value.toString());
}
};
而当我们使用gson.fromJson方法时,就会在factories中挑选合适的TypeAdapter去解析数据:
//Gson.java
public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
if (json == null) {
return null;
}
return (T) fromJson(new JsonTreeReader(json), typeOfT);
}
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
//略
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
T object = typeAdapter.read(reader);
return object;
//略
}
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
//略
try {
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
threadCalls.put(type, call);
for (TypeAdapterFactory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
typeTokenCache.put(type, candidate);
return candidate;//取得合适的TypeAdapter
}
}
throw new IllegalArgumentException("GSON cannot handle " + type);
} finally {
//略
}
}
可以在getAdapter方法看到,当json数据传入时会遍历factories并去取得对应的TypeAdapter,而后去解析。
所以当我们使用registerTypeAdapter方法时,其实就是手动添加了一个TypeAdapter以供选择:
//GsonBuilder.java
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
$Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
|| typeAdapter instanceof JsonDeserializer<?>
|| typeAdapter instanceof InstanceCreator<?>
|| typeAdapter instanceof TypeAdapter<?>);
if (typeAdapter instanceof InstanceCreator<?>) {
instanceCreators.put(type, (InstanceCreator) typeAdapter);
}
if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
TypeToken<?> typeToken = TypeToken.get(type);
factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
}
if (typeAdapter instanceof TypeAdapter<?>) {
factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
}
return this;
}
最终解析的时候,会走到调用deserialize的地方,也是JsonDeserializer之所以生效的源头:
//TreeTypeAdapter.java
@Override public T read(JsonReader in) throws IOException {
if (deserializer == null) {
return delegate().read(in);
}
JsonElement value = Streams.parse(in);
if (value.isJsonNull()) {
return null;
}
return deserializer.deserialize(value, typeToken.getType(), context);
}
以上。
边栏推荐
- Ambari2.7.5 integration es6.4.2
- 用Flink SQL流化市场数据2:盘中风险价值
- Scala初级实践——统计手机耗费流量(1)
- Using Flink SQL to fluidize market data 2: intraday var
- Use iceberg in CDP to pressurize the data Lake warehouse
- jvm学习
- UML (use case diagram, class diagram, object diagram, package diagram)
- 1.东软跨境电商数仓需求规格说明文档
- 5. Spark core programming (1)
- MYSQL基本语法字典
猜你喜欢

柠檬班软件测试培训可靠吗 这个从培训班逆袭成功的案例告诉你

MySQL cache strategy and solution

Performance bottleneck finding - Flame graph analysis

BUUCTF web WarmUp

10 question 10 answer: do you really know thread pools?

7.数据仓库搭建之数据仓库环境准备

聊聊redis分布式锁的8大坑

Redis source code analysis - data structure and Implementation (Dictionary dict)

11.数据仓库搭建之DWS层搭建

操作系统常见面试题
随机推荐
idea导入本地包
The future of data Lakehouse - Open
Operation of C language files
MySQL学习笔记(4)——(基本CRUD)操作数据库中的表的数据
C语言的指针函数
聊聊写代码的20个反面教材
Distributed registry etcd
Scala初级实践——统计手机耗费流量(1)
5. Spark核心编程(1)
C语言&位域
mysql的使用
9.数据仓库搭建之DIM层搭建
Is the software testing training of lemon class reliable? This successful case of counter attack from the training class tells you
H5 how to obtain intranet IP and public IP
1 SparkSQL概述
MySQL -- triggers and views
Data Lakehouse的未来-开放
MySQL事务
面渣逆袭:线程池夺命连环十八问,面试官直夸我
Ambari2.7.5 integration es6.4.2