简体   繁体   English

如何将DefaultMutableTreeNode(Java)序列化为JSON?

[英]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类是一个类似树的数据结构,其中包含与childrenparent相同类型的实例。 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. 它将其属性allowsChildrenuserObjectchildren为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帮助器类(具有属性allowsChildrenuserObjectchildren ),并让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使用JsonSerializerJsonDeserializer实现。 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 . 它写入/读取其属性allowsChildrenuserObjectchildren 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM