如何使用GSON反序列化Reddit API中类型动态的edited字段?
解决Reddit API中
edited字段的GSON反序列化问题 哈哈,这个坑我当初对接Reddit API的时候踩过!GSON默认的类型转换根本搞不定这种同一个字段既可能是布尔值(未编辑时为false)又可能是长整型时间戳(编辑过的情况)的场景,直接转Boolean肯定会炸。不过咱们可以通过自定义反序列化逻辑轻松解决,给你两种实用方案:
方案一:自定义JsonDeserializer(快速实现)
这种方法适合只需要反序列化的场景,逻辑直观好写。
步骤1:定义实体类
你可以把edited字段定义为Long类型——用null表示未编辑,有值则为编辑时间戳;或者封装成一个更清晰的自定义类,比如EditedInfo:
选项A:用Long直接存储
public class RedditPost { // 其他字段(比如id、title等) private Long edited; // null = 未编辑,非null = 编辑时间戳 }
选项B:封装成自定义类(更易读)
public class EditedInfo { private boolean isEdited; private Long editTimestamp; public EditedInfo(boolean isEdited, Long editTimestamp) { this.isEdited = isEdited; this.editTimestamp = editTimestamp; } // 省略getter、setter方法 } // 实体类中使用 public class RedditPost { // 其他字段 private EditedInfo edited; }
步骤2:编写自定义反序列化器
对应选项A的反序列化器
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import java.lang.reflect.Type; public class EditedLongDeserializer implements JsonDeserializer<Long> { @Override public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (json.isJsonPrimitive()) { var primitive = json.getAsJsonPrimitive(); if (primitive.isBoolean()) { // 未编辑时返回null return primitive.getAsBoolean() ? null : null; } else if (primitive.isNumber()) { // 编辑过则返回时间戳 return primitive.getAsLong(); } } // 处理异常情况,返回null或抛出异常按需选择 return null; } }
对应选项B的反序列化器
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import java.lang.reflect.Type; public class EditedInfoDeserializer implements JsonDeserializer<EditedInfo> { @Override public EditedInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (json.isJsonPrimitive()) { var primitive = json.getAsJsonPrimitive(); if (primitive.isBoolean()) { return new EditedInfo(false, null); } else if (primitive.isNumber()) { return new EditedInfo(true, primitive.getAsLong()); } } // 未知类型返回默认未编辑状态 return new EditedInfo(false, null); } }
步骤3:注册反序列化器到GSON
// 对应选项A Gson gson = new GsonBuilder() .registerTypeAdapter(Long.class, new EditedLongDeserializer()) .create(); // 对应选项B Gson gson = new GsonBuilder() .registerTypeAdapter(EditedInfo.class, new EditedInfoDeserializer()) .create(); // 开始反序列化 RedditPost post = gson.fromJson(yourJsonString, RedditPost.class);
方案二:自定义TypeAdapter(性能更优,支持序列化)
如果之后需要把数据序列化回符合Reddit格式的JSON,用TypeAdapter更合适,它的读写逻辑都能自定义,性能也比JsonDeserializer好一点。
步骤1:复用方案一中的EditedInfo类
步骤2:编写TypeAdapter
import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import java.io.IOException; public class EditedInfoTypeAdapter extends TypeAdapter<EditedInfo> { @Override public void write(JsonWriter out, EditedInfo value) throws IOException { // 序列化时:未编辑写false,编辑过写时间戳 if (value == null || !value.isEdited()) { out.value(false); } else { out.value(value.getEditTimestamp()); } } @Override public EditedInfo read(JsonReader in) throws IOException { JsonToken token = in.peek(); if (token == JsonToken.BOOLEAN) { return new EditedInfo(false, null); } else if (token == JsonToken.NUMBER) { return new EditedInfo(true, in.nextLong()); } else { // 跳过未知类型,返回默认状态 in.skipValue(); return new EditedInfo(false, null); } } }
步骤3:注册TypeAdapter
Gson gson = new GsonBuilder() .registerTypeAdapter(EditedInfo.class, new EditedInfoTypeAdapter()) .create(); // 反序列化和序列化都能用 RedditPost post = gson.fromJson(yourJsonString, RedditPost.class); String serializedJson = gson.toJson(post);
注意事项
- Reddit的
edited字段一般只有两种情况:false(未编辑)或长整型时间戳(已编辑),很少出现true的情况,所以上面的逻辑完全覆盖主流场景。 - 如果需要处理极端异常情况(比如字段是字符串),可以在反序列化器里加额外判断,避免抛出解析异常。
内容的提问来源于stack exchange,提问作者Charlie Niekirk
相关产品推荐
相关产品推荐

