【问题标题】:How to serialize DefaultMutableTreeNode (Java) to JSON?如何将 DefaultMutableTreeNode (Java) 序列化为 JSON?
【发布时间】:2019-01-01 16:33:38
【问题描述】:

如何将树(使用 DefaultMutableTreeNode 类在 Java 中实现)序列化为 JSON(通过 RESTful 方法传输到 iOS 客户端)?

我试过了:

String jsonString = (new Gson()).toJson(topNode);
// topNode is DefaultMutableTreeNode at the root

它与StackOverflowError 崩溃。

【问题讨论】:

    标签: java json serialization gson jtree


    【解决方案1】:

    Swing 的DefaultMutableTreeNode 类是树状数据结构 其中包含与childrenparent 相同类型的实例。 这就是为什么 Gson 的默认序列化程序会陷入无限递归 因此抛出了StackOverflowError

    要解决此问题,您需要使用更智能的 JsonSerializer 自定义您的 Gson 专门用于将DefaultMutableTreeNode 转换为 JSON。 作为奖励,您可能还想提供JsonDeserializer 用于将此类 JSON 转换回 DefaultMutableTreeNode

    为此,不仅可以通过new Gson() 创建您的Gson 实例,还可以通过

    Gson gson = new GsonBuilder()
            .registerTypeAdapter(DefaultMutableTreeNode.class, new DefaultMutableTreeNodeSerializer())
            .registerTypeAdapter(DefaultMutableTreeNode.class, new DefaultMutableTreeNodeDeserializer())
            .setPrettyPrinting()
            .create();
    

    下方DefaultMutableTreeNodeSerializer负责 用于将DefaultMutableTreeNode 转换为 JSON。 它将其属性allowsChildrenuserObjectchildren 转换为JSON。 请注意,它不会将 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;
        }
    }
    

    为了测试,让我们将样本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);
    

    它会生成以下 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"
            }
          ]
        }
      ]
    }
    

    下方DefaultMutableTreeNodeDeserializer负责 用于将 JSON 转换回 DefaultMutableTreeNode

    它使用与反序列化器相同的想法 How to serialize/deserialize a DefaultMutableTreeNode with Jackson?DefaultMutableTreeNode 不是很像 POJO,因此不会 与 Gson 合作良好。 因此它使用了一个行为良好的 POJO 助手类(带有属性 allowsChildrenuserObjectchildren) 并让 Gson 将 JSON 内容反序列化到此类中。 然后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;
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      这是我旧答案的改进替代方案,旧答案使用JsonSerializerJsonDeserializer 的实现DefaultMutableTreeNode。 这两个接口的 API 文档说:

      新应用程序应该更喜欢TypeAdapter,它的流式API 比这个接口的树形 API 更高效。

      因此,让我们使用这种首选方法并实现一个 TypeAdapterDefaultMutableTreeNode

      为了使用它,您可以像这样创建 Gson 实例 (而不仅仅是使用new Gson()):

      Gson gson = new GsonBuilder()
              .registerTypeAdapterFactory(DefaultMutableTreeNodeTypeAdapter.FACTORY)
              .setPrettyPrinting()
              .create();
      

      下面的DefaultMutableTreeNodeTypeAdapter负责 将DefaultMutableTreeNode 与 JSON 相互转换。 它写入/读取其属性allowsChildrenuserObjectchildren。 不需要写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;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-07-28
        • 2020-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-10
        • 2013-08-20
        相关资源
        最近更新 更多