java – Gson:如何处理可能具有不同类型的字段?
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了java – Gson:如何处理可能具有不同类型的字段?,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5247字,纯文字阅读大概需要8分钟。
内容图文
![java – Gson:如何处理可能具有不同类型的字段?](/upload/InfoBanner/zyjiaocheng/774/441a95cb345245da96a8f49258a9e27d.jpg)
我正在尝试使用Gson反序列化响应.数据由可以嵌套到任意深度的节点列表组成. json看起来像这样:
{
"type": "node",
"children": [
{
"id": "abc123",
"name": "Name 1",
"subdata": {
"type": "node",
"children": [
{
"id": "def456",
"name": "Name 2"
}
]
}
}
]
}
现在,没有任何自定义类型适配器,我可以使用以下类:
public class Data {
private String type;
private List<Node> nodes;
}
public class Node {
private String id;
private String name;
private Data subdata;
}
现在一切都很好,花花公子.但是,服务器可能会切断一些较深的节点并仅响应其ID,因此子数据可能如下所示:
"subdata": {
"type": "extra",
"children": ["ghi", "jkl", "mno"]
}
这当然可以表示为这样的Java类:
public class ExtraData {
private String type;
private List<String> children;
}
但问题是:如何处理反序列化以使子数据可以是Data或ExtraData?
解决方法:
给定节点的子节点似乎总是JSON数组,所以你可以用它们做的第一件事就是将子节点声明为List<?>隐藏实际类型.但是,您仍然可以使用类型属性/字段来获取子项的实际类型.最简单的方法可能就是添加另一个JSON反序列化器,以便以一些性能成本反序列化Data实例(因为这些不是类型适配器),据我所知,在Data类的字段上缺少@SerializedName.
如果你也可以改变你的DTO类型,那么更喜欢枚举而不是原始字符串,因为它们与枚举完美配合(特别是与智能IDE配合使用):
enum Type {
@SerializedName("node")
NODE,
@SerializedName("extra")
EXTRA
}
然后,Data类本身可能如下所示:
final class Data {
private final Type type;
private final List<?> children; // this one is supposed to be:
// * either List<String> if type=EXTRA
// * or List<Node> if type=NODE
Data(final Type type, final List<?> children) {
this.type = type;
this.children = children;
}
Type getType() {
return type;
}
List<?> getChildren() {
return children;
}
}
由于额外输入的子项只是您问题中的字符串,只需添加节点DTO类:
final class Node {
@SerializedName("id")
private final String id = null;
@SerializedName("name")
private final String name = null;
@SerializedName("subdata")
private final Data subdata = null;
String getId() {
return id;
}
String getName() {
return name;
}
Data getSubdata() {
return subdata;
}
}
现在,在反序列化Data类时,您可以确定子列表的实际类型,并根据节点类型将其反序列化为字符串列表或节点列表.请注意,下面的反序列化器使用java.lang.reflect.Type实例而不是java.lang.Class,因为后者由于Java泛型类型擦除而较弱,而对于任何列表参数化(字符串,节点等)都是List.class.使用类型标记提供预期类型,只需将JSON键/值对委托给指定目标类型的反序列化上下文,从而进行适用于任意嵌套元素级别的递归反序列化(但是,GSON有一些内部堆栈限制,限制为32如果我没错的话).
final class DataJsonDeserializer
implements JsonDeserializer<Data> {
private static final JsonDeserializer<Data> dataJsonDeserializer = new DataJsonDeserializer();
private static final java.lang.reflect.Type nodeListType = new TypeToken<List<Node>>() {
}.getType();
private static final java.lang.reflect.Type stringListType = new TypeToken<List<String>>() {
}.getType();
private DataJsonDeserializer() {
}
static JsonDeserializer<Data> getDataJsonDeserializer() {
return dataJsonDeserializer;
}
@Override
public Data deserialize(final JsonElement jsonElement, final java.lang.reflect.Type type, final JsonDeserializationContext context)
throws JsonParseException {
final JsonObject rootJsonObject = jsonElement.getAsJsonObject();
final Type nodeType = context.deserialize(rootJsonObject.get("type"), Type.class);
final JsonArray childrenJsonArray = rootJsonObject.get("children").getAsJsonArray();
final List<?> children;
switch ( nodeType ) {
case NODE:
children = context.deserialize(childrenJsonArray, nodeListType);
break;
case EXTRA:
children = context.deserialize(childrenJsonArray, stringListType);
break;
default:
throw new AssertionError(nodeType);
}
return new Data(nodeType, children);
}
}
以递归方式遍历子项的演示(请注意下面将每个项目强制转换为目标类型的增强for语句):
public final class EntryPoint {
private static final String JSON_WITH_SUBNODES = "{\"type\":\"node\",\"children\":[{\"id\":\"abc123\",\"name\":\"Name 1\",\"subdata\":{\"type\":\"node\",\"children\":[{\"id\":\"def456\",\"name\":\"Name 2\"}]}}]}";
private static final String JSON_WITH_REFERENCES = "{\"type\":\"node\",\"children\":[{\"id\":\"abc123\",\"name\":\"Name 1\",\"subdata\":{\"type\":\"extra\",\"children\":[\"ghi\",\"jkl\",\"mno\"]}}]}";
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(Data.class, getDataJsonDeserializer())
.create();
public static void main(final String... args) {
process(gson.fromJson(JSON_WITH_SUBNODES, Data.class));
process(gson.fromJson(JSON_WITH_REFERENCES, Data.class));
}
private static void process(final Data data) {
process(data, 0);
out.println();
}
private static void process(final Data data, final int level) {
for ( int i = 0; i < level; i++ ) {
out.print('>');
}
final List<?> children = data.getChildren();
final Type type = data.getType();
out.println(type);
switch ( type ) {
case NODE:
@SuppressWarnings("unchecked")
final Iterable<Node> nodeChildren = (Iterable<Node>) children;
for ( final Node node : nodeChildren ) {
out.printf("\t%s %s\n", node.getId(), node.getName());
final Data subdata = node.getSubdata();
if ( subdata != null ) {
process(subdata, level + 1);
}
}
break;
case EXTRA:
@SuppressWarnings("unchecked")
final Iterable<String> extraChildren = (Iterable<String>) children;
for ( final String extra : extraChildren ) {
out.printf("\t%s\n", extra);
}
break;
default:
throw new AssertionError(type);
}
}
}
输出:
NODE
abc123 Name 1
>NODE
def456 Name 2
NODE
abc123 Name 1
>EXTRA
ghi
jkl
mno
内容总结
以上是互联网集市为您收集整理的java – Gson:如何处理可能具有不同类型的字段?全部内容,希望文章能够帮你解决java – Gson:如何处理可能具有不同类型的字段?所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。