[英]How to serialize DefaultMutableTreeNode (Java) to JSON?
How can I serialize a tree (implemented in Java using the DefaultMutableTreeNode
class) to JSON (for transferring via RESTful method to an iOS client)? 如何将树(使用
DefaultMutableTreeNode
类在Java中实现)序列化为JSON(用于通过RESTful方法传输到iOS客户端)?
I tried: 我试过了:
String jsonString = (new Gson()).toJson(topNode);
// topNode is DefaultMutableTreeNode at the root
It crashed with StackOverflowError
. 它崩溃与
StackOverflowError
。
Swing's DefaultMutableTreeNode
class is a tree-like data structure which contains instances of this same type both as children
and as parent
. Swing的
DefaultMutableTreeNode
类是一个类似树的数据结构,其中包含与children
和parent
相同类型的实例。 That's why Gson's default serializer ran into infinite recursion and hence threw a StackOverflowError
. 这就是Gson的默认序列化程序遇到无限递归并因此引发
StackOverflowError
。
To solve this problem you need to customize your Gson
with a smarter JsonSerializer
specially crafted for converting a DefaultMutableTreeNode
to JSON. 要解决此问题,您需要使用专门设计用于将
DefaultMutableTreeNode
转换为JSON的更智能的JsonSerializer
来定制Gson
。 As a bonus you might also want to provide a JsonDeserializer
for converting such JSON back to a DefaultMutableTreeNode
. 另外,您可能还想提供一个
JsonDeserializer
用于将此类JSON转换回DefaultMutableTreeNode
。
For that create your Gson
instance not just by new Gson()
, but by 为此,不仅要通过
new Gson()
创建Gson
实例,还要通过
Gson gson = new GsonBuilder()
.registerTypeAdapter(DefaultMutableTreeNode.class, new DefaultMutableTreeNodeSerializer())
.registerTypeAdapter(DefaultMutableTreeNode.class, new DefaultMutableTreeNodeDeserializer())
.setPrettyPrinting()
.create();
The DefaultMutableTreeNodeSerializer
below is responsible for converting a DefaultMutableTreeNode
to JSON. 下面的
DefaultMutableTreeNodeSerializer
负责将DefaultMutableTreeNode
转换为JSON。 It converts its properties allowsChildren
, userObject
and children
to JSON. 它将其属性
allowsChildren
, userObject
和children
为JSON。 Note that it does not convert the parent
property to JSON, because doing that would produce an inifinite recursion again. 请注意,它不会将
parent
属性转换为JSON,因为这样做会再次产生无限递归。
public class DefaultMutableTreeNodeSerializer implements JsonSerializer<DefaultMutableTreeNode> {
@Override
public JsonElement serialize(DefaultMutableTreeNode src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("allowsChildren", src.getAllowsChildren());
jsonObject.add("userObject", context.serialize(src.getUserObject()));
if (src.getChildCount() > 0) {
jsonObject.add("children", context.serialize(Collections.list(src.children())));
}
return jsonObject;
}
}
For testing let us serialize the root node of a sample JTree
to JSON, and then deserialize it again. 为了进行测试,让我们将示例
JTree
的根节点序列化为JSON,然后再次将其反序列化。
JTree tree = new JTree(); // create a sample tree
Object topNode = tree.getModel().getRoot(); // a DefaultMutableTreeNode
String jsonString = gson.toJson(topNode);
System.out.println(jsonString);
DefaultMutableTreeNode topNode2 = gson.fromJson(jsonString, DefaultMutableTreeNode.class);
It generates the following JSON output: 它生成以下JSON输出:
{
"allowsChildren": true,
"userObject": "JTree",
"children": [
{
"allowsChildren": true,
"userObject": "colors",
"children": [
{
"allowsChildren": true,
"userObject": "blue"
},
{
"allowsChildren": true,
"userObject": "violet"
},
{
"allowsChildren": true,
"userObject": "red"
},
{
"allowsChildren": true,
"userObject": "yellow"
}
]
},
{
"allowsChildren": true,
"userObject": "sports",
"children": [
{
"allowsChildren": true,
"userObject": "basketball"
},
{
"allowsChildren": true,
"userObject": "soccer"
},
{
"allowsChildren": true,
"userObject": "football"
},
{
"allowsChildren": true,
"userObject": "hockey"
}
]
},
{
"allowsChildren": true,
"userObject": "food",
"children": [
{
"allowsChildren": true,
"userObject": "hot dogs"
},
{
"allowsChildren": true,
"userObject": "pizza"
},
{
"allowsChildren": true,
"userObject": "ravioli"
},
{
"allowsChildren": true,
"userObject": "bananas"
}
]
}
]
}
The DefaultMutableTreeNodeDeserializer
below is responsible for converting JSON back to a DefaultMutableTreeNode
. 下面的
DefaultMutableTreeNodeDeserializer
负责将JSON转换回DefaultMutableTreeNode
。
It uses the same idea as the deserializer from How to serialize/deserialize a DefaultMutableTreeNode with Jackson? 它使用与如何使用Jackson序列化/反序列化DefaultMutableTreeNode中的反序列化器相同的思想。 .
。 The
DefaultMutableTreeNode
is not very POJO-like and thus doesn't work well together with Gson. DefaultMutableTreeNode
不是非常类似于POJO,因此不能与Gson一起很好地工作。 Therefore it uses a well-behaving POJO
helper class (with properties allowsChildren
, userObject
and children
) and lets Gson deserialize the JSON content into this class. 因此,它使用行为良好的
POJO
帮助器类(具有属性allowsChildren
, userObject
和children
),并让Gson将JSON内容反序列化到此类中。 Then the POJO
object (and its POJO
children) is converted to a DefaultMutableTreeNode
object (with DefaultMutableTreeNode
children). 然后,将
POJO
对象(及其POJO
子对象)转换为DefaultMutableTreeNode
对象(具有DefaultMutableTreeNode
子对象)。
public class DefaultMutableTreeNodeDeserializer implements JsonDeserializer<DefaultMutableTreeNode> {
@Override
public DefaultMutableTreeNode deserialize(JsonElement json, Type type, JsonDeserializationContext context) {
return context.<POJO>deserialize(json, POJO.class).toDefaultMutableTreeNode();
}
private static class POJO {
private boolean allowsChildren;
private Object userObject;
private List<POJO> children;
// no need for: POJO parent
public DefaultMutableTreeNode toDefaultMutableTreeNode() {
DefaultMutableTreeNode node = new DefaultMutableTreeNode();
node.setAllowsChildren(allowsChildren);
node.setUserObject(userObject);
if (children != null) {
for (POJO child : children) {
node.add(child.toDefaultMutableTreeNode()); // recursion!
// this did also set the parent of the child-node
}
}
return node;
}
// Following setters needed by Gson's deserialization:
public void setAllowsChildren(boolean allowsChildren) {
this.allowsChildren = allowsChildren;
}
public void setUserObject(Object userObject) {
this.userObject = userObject;
}
public void setChildren(List<POJO> children) {
this.children = children;
}
}
}
This is an improved alternative to my older answer which used implementations of JsonSerializer
and JsonDeserializer
for DefaultMutableTreeNode
. 这是对我的较早答案的一种改进的替代方法,该较早的答案对
DefaultMutableTreeNode
使用JsonSerializer
和JsonDeserializer
实现。 The API doc of these 2 interfaces says: 这两个接口的API文档说:
New applications should prefer
TypeAdapter
, whose streaming API is more efficient than this interface's tree API.新应用程序应该首选
TypeAdapter
,其流API比此接口的树API更高效。
Let's therefore use this preferred approach and implement a TypeAdapter
for DefaultMutableTreeNode
. 因此,让我们使用这种首选方法,并为
DefaultMutableTreeNode
实现TypeAdapter
。
For using it you create your Gson
instance like this (instead of just using new Gson()
): 使用它,您可以像这样创建
Gson
实例(而不是仅使用new Gson()
):
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(DefaultMutableTreeNodeTypeAdapter.FACTORY)
.setPrettyPrinting()
.create();
The DefaultMutableTreeNodeTypeAdapter
below is responsible for converting a DefaultMutableTreeNode
to and from JSON. 下面的
DefaultMutableTreeNodeTypeAdapter
负责将DefaultMutableTreeNode
与JSON相互转换。 It writes/reads its properties allowsChildren
, userObject
and children
. 它写入/读取其属性
allowsChildren
, userObject
和children
。 There is no need to write the parent
property, because the parent-child relations are already encoded in the nested structure of the JSON-output. 无需编写
parent
属性,因为父子关系已经在JSON输出的嵌套结构中进行了编码。
public class DefaultMutableTreeNodeTypeAdapter extends TypeAdapter<DefaultMutableTreeNode> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@Override
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (type.getRawType() == DefaultMutableTreeNode.class) {
return (TypeAdapter<T>) new DefaultMutableTreeNodeTypeAdapter(gson);
}
return null;
}
};
private final Gson gson;
private DefaultMutableTreeNodeTypeAdapter(Gson gson) {
this.gson = gson;
}
@Override
public void write(JsonWriter out, DefaultMutableTreeNode node) throws IOException {
out.beginObject();
out.name("allowsChildren");
out.value(node.getAllowsChildren());
out.name("userObject");
gson.toJson(node.getUserObject(), Object.class, out);
if (node.getChildCount() > 0) {
out.name("children");
gson.toJson(Collections.list(node.children()), List.class, out); // recursion!
}
// No need to write node.getParent(), it would lead to infinite recursion.
out.endObject();
}
@Override
public DefaultMutableTreeNode read(JsonReader in) throws IOException {
in.beginObject();
DefaultMutableTreeNode node = new DefaultMutableTreeNode();
while (in.hasNext()) {
switch (in.nextName()) {
case "allowsChildren":
node.setAllowsChildren(in.nextBoolean());
break;
case "userObject":
node.setUserObject(gson.fromJson(in, Object.class));
break;
case "children":
in.beginArray();
while (in.hasNext()) {
node.add(read(in)); // recursion!
// this did also set the parent of the child-node
}
in.endArray();
break;
default:
in.skipValue();
break;
}
}
in.endObject();
return node;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.