java - Gson:基于另一个字段的动态字段解析
问题描述
我有以下两种类型的 JSON 字符串,它们都有一个字段“type”,但“content”字段的结构不同。
{"type": "Login", "content": {"username": "a", "password": "b"}}
{"type": "Forward", "content": {"deviceId": "a", "password": "b"}}
我想将它们解析为 Java 对象。现在我有两个班:
class Login {
String username;
String password;
}
class Forward {
String deviceId;
}
现在的问题是,我必须先解析 json 消息的“部分”(即“类型”部分),然后才能决定使用哪个类来继续解析“内容”。
有没有办法进行这种“两步”解析?
我尝试了以下方法:
首先将json消息解析为此类的一个实例:
class Request {
RequesType type;
String content;
}
然后根据类的解析“类型”字段,我可以决定使用哪个类,即使用res = gson.fromJson(request.content, Forward.class);
或res = gson.fromJson(content, Login.class)
;
但是我收到的传入json是
{"type": "Forward", "content":{"deviceId":"1"}}
我得到了预期的错误是 String 但是 Object 所以我不知道如何继续。任何建议将不胜感激!
解决方案
也许您想使用 GSON 的RuntimeTypeAdapterFactory。它允许您从 type 字段确定要自动反序列化的类的类型:默认命名为type很方便。
为了让 GSON 具有反序列化的继承权,我建议对您的 POJO 进行一些小的更改。创建类为Request
:
@Getter @Setter
public class Request {
private String type;
@Getter @Setter
public static class Content {
String password;
}
}
覆盖它和每个请求类型的内容,例如:
@Getter @Setter
public class ForwardRequest extends Request {
@Getter @Setter
public static class ForwardContent extends Content {
private String deviceId;
}
private ForwardContent content;
}
和
@Getter @Setter
public class LoginRequest extends Request {
@Getter @Setter
public static class LoginContent extends Content {
private String username;
}
private LoginContent content;
}
对于上面的类,它只是:
@Test
public void test() {
// Tell the top class
RuntimeTypeAdapterFactory<Request> rttaf = RuntimeTypeAdapterFactory.of(Request.class)
// Register inheriting types and the values per type field
.registerSubtype(ForwardRequest.class, "Forward")
.registerSubtype(LoginRequest.class, "Login");
Gson gson = new GsonBuilder().setPrettyPrinting()
.registerTypeAdapterFactory(rttaf)
.create();
// Constructed an array of your two types to be deserialized at the same time
String jsonArr = "["
+ "{\"type\": \"Login\", \"content\": {\"username\": \"a\", \"password\": \"b\"}}"
+ ","
+ "{\"type\": \"Forward\", \"content\": {\"deviceId\": \"a\", \"password\": \"b\"}}"
+ "]";
// Deserialize the array as Request[]
Request[] requests = gson.fromJson(jsonArr, Request[].class);
log.info("{}", requests[0].getClass());
log.info("{}", requests[1].getClass());
}
以上的输出将类似于:
类 org.example.gson.LoginRequest
类 org.example.gson.ForwardRequest
您只需复制答案顶部链接中提供的文件即可包含RuntimeTypeAdapterFactory
到您的项目中。
更新:关于反序列type
化或包含有关类型信息的任何其他字段:GSON 故意将其省略。它不是一种瞬态场吗?您需要在type
反序列化之前知道 的值,这样 GSON 就不再费心去反序列化它了。
作为另一个例子 - 为了澄清这一点 - 如果您更改测试字符串,例如:
String jsonArr = "["
+ "{\"type\": \"LoginRequest\", \"content\": {\"username\": \"a\", \"password\": \"b\"}}"
+ ","
+ "{\"type\": \"ForwardRequest\", \"content\": {\"deviceId\": \"a\", \"password\": \"b\"}}"
+ "]";
因此该类型包含简单的类名称(通常是这种情况),您可以像这样注册子类型:
.registerSubtype(ForwardRequest.class)
.registerSubtype(LoginRequest.class);
并且 GSON 期望类简单名称作为 JSON 类型属性的值。为什么要将类名保存在单独的字段中,因为它是可获取的Class.getSimpleName()
?
但是,您当然有时可能需要将其序列化给其他客户端。
推荐阅读
- reactjs - 部署后反应不显示图像
- groovy - 在 Jmeter Groovy 中读取请求 Cokkie
- python - 如何使用外键/多键编写测试 - Django
- reactjs - 使用 FormData 从 React 应用程序上传 Sailsjs 文件不起作用
- r - 如何从向量中获得概率密度函数?
- python - 如何对采用列表列平均值的熊猫数据框执行 groupby 操作?
- keycloak - 如何在访问令牌中指定特定于客户端的自定义属性
- reactjs - 在服务器中保存文件 - React.js
- dpdk - 无法识别多个 IB 设备上的主/代表
- node.js - mongodb部分号搜索