How to serialize DefaultMutableTreeNode (Java) to JSON?












0















How can I serialize a tree (implemented in Java using the DefaultMutableTreeNode class) to JSON (for transferring via RESTful method to an iOS client)?



I tried:



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


It crashed with StackOverflowError.










share|improve this question





























    0















    How can I serialize a tree (implemented in Java using the DefaultMutableTreeNode class) to JSON (for transferring via RESTful method to an iOS client)?



    I tried:



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


    It crashed with StackOverflowError.










    share|improve this question



























      0












      0








      0


      0






      How can I serialize a tree (implemented in Java using the DefaultMutableTreeNode class) to JSON (for transferring via RESTful method to an iOS client)?



      I tried:



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


      It crashed with StackOverflowError.










      share|improve this question
















      How can I serialize a tree (implemented in Java using the DefaultMutableTreeNode class) to JSON (for transferring via RESTful method to an iOS client)?



      I tried:



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


      It crashed with StackOverflowError.







      java json serialization gson jtree






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 2 at 1:13









      Thomas Fritsch

      5,436122135




      5,436122135










      asked Jan 1 at 16:33









      ikevin8meikevin8me

      1,94722960




      1,94722960
























          2 Answers
          2






          active

          oldest

          votes


















          2














          Swing's DefaultMutableTreeNode class is a tree-like data structure
          which contains instances of this same type both as children and as parent.
          That's why Gson's default serializer ran into infinite recursion
          and hence threw a StackOverflowError.



          To solve this problem you need to customize your Gson with a smarter JsonSerializer
          specially crafted for converting a DefaultMutableTreeNode to JSON.
          As a bonus you might also want to provide a JsonDeserializer
          for converting such JSON back to a DefaultMutableTreeNode.



          For that create your Gson instance not just by new Gson(), but by



          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.
          It converts its properties allowsChildren, userObject and children to JSON.
          Note that it does not convert the parent property to JSON,
          because doing that would produce an inifinite recursion again.



          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.



          tree



          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:



          {
          "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.



          It uses the same idea as the deserializer from
          How to serialize/deserialize a DefaultMutableTreeNode with Jackson?.
          The DefaultMutableTreeNode is not very POJO-like and thus doesn't
          work well together with 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.
          Then the POJO object (and its POJO children) is converted to a
          DefaultMutableTreeNode object (with DefaultMutableTreeNode children).



          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;
          }
          }
          }





          share|improve this answer

































            0














            This is an improved alternative to my older answer which used implementations of JsonSerializer and JsonDeserializer for DefaultMutableTreeNode.
            The API doc of these 2 interfaces says:




            New applications should prefer TypeAdapter, whose streaming API
            is more efficient than this interface's tree API.




            Let's therefore use this preferred approach and implement a
            TypeAdapter for DefaultMutableTreeNode.



            For using it you create your Gson instance like this
            (instead of just using new Gson()):



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


            The DefaultMutableTreeNodeTypeAdapter below is responsible for
            converting a DefaultMutableTreeNode to and from JSON.
            It writes/reads its properties allowsChildren, userObject and 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.



            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");
            out.beginArray();
            for (Object childNode : Collections.list(node.children())) {
            gson.toJson(childNode, Object.class, out); // recursion!
            }
            out.endArray();
            }
            // 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;
            }
            }





            share|improve this answer























              Your Answer






              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "1"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              draft saved

              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53997112%2fhow-to-serialize-defaultmutabletreenode-java-to-json%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              2














              Swing's DefaultMutableTreeNode class is a tree-like data structure
              which contains instances of this same type both as children and as parent.
              That's why Gson's default serializer ran into infinite recursion
              and hence threw a StackOverflowError.



              To solve this problem you need to customize your Gson with a smarter JsonSerializer
              specially crafted for converting a DefaultMutableTreeNode to JSON.
              As a bonus you might also want to provide a JsonDeserializer
              for converting such JSON back to a DefaultMutableTreeNode.



              For that create your Gson instance not just by new Gson(), but by



              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.
              It converts its properties allowsChildren, userObject and children to JSON.
              Note that it does not convert the parent property to JSON,
              because doing that would produce an inifinite recursion again.



              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.



              tree



              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:



              {
              "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.



              It uses the same idea as the deserializer from
              How to serialize/deserialize a DefaultMutableTreeNode with Jackson?.
              The DefaultMutableTreeNode is not very POJO-like and thus doesn't
              work well together with 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.
              Then the POJO object (and its POJO children) is converted to a
              DefaultMutableTreeNode object (with DefaultMutableTreeNode children).



              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;
              }
              }
              }





              share|improve this answer






























                2














                Swing's DefaultMutableTreeNode class is a tree-like data structure
                which contains instances of this same type both as children and as parent.
                That's why Gson's default serializer ran into infinite recursion
                and hence threw a StackOverflowError.



                To solve this problem you need to customize your Gson with a smarter JsonSerializer
                specially crafted for converting a DefaultMutableTreeNode to JSON.
                As a bonus you might also want to provide a JsonDeserializer
                for converting such JSON back to a DefaultMutableTreeNode.



                For that create your Gson instance not just by new Gson(), but by



                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.
                It converts its properties allowsChildren, userObject and children to JSON.
                Note that it does not convert the parent property to JSON,
                because doing that would produce an inifinite recursion again.



                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.



                tree



                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:



                {
                "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.



                It uses the same idea as the deserializer from
                How to serialize/deserialize a DefaultMutableTreeNode with Jackson?.
                The DefaultMutableTreeNode is not very POJO-like and thus doesn't
                work well together with 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.
                Then the POJO object (and its POJO children) is converted to a
                DefaultMutableTreeNode object (with DefaultMutableTreeNode children).



                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;
                }
                }
                }





                share|improve this answer




























                  2












                  2








                  2







                  Swing's DefaultMutableTreeNode class is a tree-like data structure
                  which contains instances of this same type both as children and as parent.
                  That's why Gson's default serializer ran into infinite recursion
                  and hence threw a StackOverflowError.



                  To solve this problem you need to customize your Gson with a smarter JsonSerializer
                  specially crafted for converting a DefaultMutableTreeNode to JSON.
                  As a bonus you might also want to provide a JsonDeserializer
                  for converting such JSON back to a DefaultMutableTreeNode.



                  For that create your Gson instance not just by new Gson(), but by



                  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.
                  It converts its properties allowsChildren, userObject and children to JSON.
                  Note that it does not convert the parent property to JSON,
                  because doing that would produce an inifinite recursion again.



                  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.



                  tree



                  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:



                  {
                  "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.



                  It uses the same idea as the deserializer from
                  How to serialize/deserialize a DefaultMutableTreeNode with Jackson?.
                  The DefaultMutableTreeNode is not very POJO-like and thus doesn't
                  work well together with 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.
                  Then the POJO object (and its POJO children) is converted to a
                  DefaultMutableTreeNode object (with DefaultMutableTreeNode children).



                  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;
                  }
                  }
                  }





                  share|improve this answer















                  Swing's DefaultMutableTreeNode class is a tree-like data structure
                  which contains instances of this same type both as children and as parent.
                  That's why Gson's default serializer ran into infinite recursion
                  and hence threw a StackOverflowError.



                  To solve this problem you need to customize your Gson with a smarter JsonSerializer
                  specially crafted for converting a DefaultMutableTreeNode to JSON.
                  As a bonus you might also want to provide a JsonDeserializer
                  for converting such JSON back to a DefaultMutableTreeNode.



                  For that create your Gson instance not just by new Gson(), but by



                  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.
                  It converts its properties allowsChildren, userObject and children to JSON.
                  Note that it does not convert the parent property to JSON,
                  because doing that would produce an inifinite recursion again.



                  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.



                  tree



                  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:



                  {
                  "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.



                  It uses the same idea as the deserializer from
                  How to serialize/deserialize a DefaultMutableTreeNode with Jackson?.
                  The DefaultMutableTreeNode is not very POJO-like and thus doesn't
                  work well together with 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.
                  Then the POJO object (and its POJO children) is converted to a
                  DefaultMutableTreeNode object (with DefaultMutableTreeNode children).



                  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;
                  }
                  }
                  }






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Mar 8 at 18:37

























                  answered Jan 2 at 0:50









                  Thomas FritschThomas Fritsch

                  5,436122135




                  5,436122135

























                      0














                      This is an improved alternative to my older answer which used implementations of JsonSerializer and JsonDeserializer for DefaultMutableTreeNode.
                      The API doc of these 2 interfaces says:




                      New applications should prefer TypeAdapter, whose streaming API
                      is more efficient than this interface's tree API.




                      Let's therefore use this preferred approach and implement a
                      TypeAdapter for DefaultMutableTreeNode.



                      For using it you create your Gson instance like this
                      (instead of just using new Gson()):



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


                      The DefaultMutableTreeNodeTypeAdapter below is responsible for
                      converting a DefaultMutableTreeNode to and from JSON.
                      It writes/reads its properties allowsChildren, userObject and 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.



                      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");
                      out.beginArray();
                      for (Object childNode : Collections.list(node.children())) {
                      gson.toJson(childNode, Object.class, out); // recursion!
                      }
                      out.endArray();
                      }
                      // 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;
                      }
                      }





                      share|improve this answer




























                        0














                        This is an improved alternative to my older answer which used implementations of JsonSerializer and JsonDeserializer for DefaultMutableTreeNode.
                        The API doc of these 2 interfaces says:




                        New applications should prefer TypeAdapter, whose streaming API
                        is more efficient than this interface's tree API.




                        Let's therefore use this preferred approach and implement a
                        TypeAdapter for DefaultMutableTreeNode.



                        For using it you create your Gson instance like this
                        (instead of just using new Gson()):



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


                        The DefaultMutableTreeNodeTypeAdapter below is responsible for
                        converting a DefaultMutableTreeNode to and from JSON.
                        It writes/reads its properties allowsChildren, userObject and 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.



                        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");
                        out.beginArray();
                        for (Object childNode : Collections.list(node.children())) {
                        gson.toJson(childNode, Object.class, out); // recursion!
                        }
                        out.endArray();
                        }
                        // 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;
                        }
                        }





                        share|improve this answer


























                          0












                          0








                          0







                          This is an improved alternative to my older answer which used implementations of JsonSerializer and JsonDeserializer for DefaultMutableTreeNode.
                          The API doc of these 2 interfaces says:




                          New applications should prefer TypeAdapter, whose streaming API
                          is more efficient than this interface's tree API.




                          Let's therefore use this preferred approach and implement a
                          TypeAdapter for DefaultMutableTreeNode.



                          For using it you create your Gson instance like this
                          (instead of just using new Gson()):



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


                          The DefaultMutableTreeNodeTypeAdapter below is responsible for
                          converting a DefaultMutableTreeNode to and from JSON.
                          It writes/reads its properties allowsChildren, userObject and 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.



                          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");
                          out.beginArray();
                          for (Object childNode : Collections.list(node.children())) {
                          gson.toJson(childNode, Object.class, out); // recursion!
                          }
                          out.endArray();
                          }
                          // 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;
                          }
                          }





                          share|improve this answer













                          This is an improved alternative to my older answer which used implementations of JsonSerializer and JsonDeserializer for DefaultMutableTreeNode.
                          The API doc of these 2 interfaces says:




                          New applications should prefer TypeAdapter, whose streaming API
                          is more efficient than this interface's tree API.




                          Let's therefore use this preferred approach and implement a
                          TypeAdapter for DefaultMutableTreeNode.



                          For using it you create your Gson instance like this
                          (instead of just using new Gson()):



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


                          The DefaultMutableTreeNodeTypeAdapter below is responsible for
                          converting a DefaultMutableTreeNode to and from JSON.
                          It writes/reads its properties allowsChildren, userObject and 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.



                          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");
                          out.beginArray();
                          for (Object childNode : Collections.list(node.children())) {
                          gson.toJson(childNode, Object.class, out); // recursion!
                          }
                          out.endArray();
                          }
                          // 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;
                          }
                          }






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered 8 hours ago









                          Thomas FritschThomas Fritsch

                          5,436122135




                          5,436122135






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Stack Overflow!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53997112%2fhow-to-serialize-defaultmutabletreenode-java-to-json%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              Can a sorcerer learn a 5th-level spell early by creating spell slots using the Font of Magic feature?

                              Does disintegrating a polymorphed enemy still kill it after the 2018 errata?

                              A Topological Invariant for $pi_3(U(n))$