mardi 15 décembre 2020

Deserialization of Many Different Node Types

I have a big JSON tree I'd like to parse using Java. It consists of many different nodes types, no the node types in general have different properties. I'd like to use the Jackson or Gson libraries to extract different property values out of the tree depending on the node type. I have already managed to parse the Json tree into a Gson JsonElement but to read out the values of the nodes, some pattern, like a visitor, might be useful. I did not find anything in this regard in the documentation of Gson or Jackson.

I tried to do it recursively, but this doesn't work in the end due to all the different nodetypes which can not be distinguished, since in Gson and Jackson they both are JsonElements, or JsonNodes respectively.

What is the best way/pattern to deserialize such a complex tree? Possibly with an example about how to setup for the below tree.

And yes, I saw this post, which seems to be operating on just 2 different node types.

// Working code since tree nodes are of uniform type here, but thats not the case in the tree below:

import com.google.gson.*;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class Main {

    private class SomePojo {
        private String name;
        private int id;
        private List<SomePojo> children;

        public SomePojo(String name, int id, List<SomePojo> children) {
            this.name = name;
            this.id = id;
            this.children = children;
        }

        @Override
        public String toString() {
            return new GsonBuilder().setPrettyPrinting().create().toJson(this, SomePojo.class);
        }
    }

    private class SomePojoDeserializer implements JsonDeserializer<SomePojo> {

        public SomePojo deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
            JsonObject object = jsonElement.getAsJsonObject();
            String name = object.get("name").getAsString();
            int id = object.get("id").getAsInt();
            JsonArray children = object.getAsJsonArray("children");
            List<SomePojo> childrenPojos = new ArrayList<SomePojo>();
            for (int i = 0; i < children.size(); i++) {
                JsonElement childjsonelement = children.get(i);
                SomePojo child = deserialize(childjsonelement, type, jsonDeserializationContext);
                childrenPojos.add(child);
                }
            SomePojo pojo = new SomePojo();
            pojo.setName(name);
            pojo.setId((double) id);
            pojo.setChildren(childrenPojos);
            return pojo;
        }
    }

    public static void main(String[] args) {

        String testjson = "{" +
                "name: Some, " +
                "id: 0, " +
                "children: [" +
                "   {" +
                        "name: SomeChild1, " +
                        "id: 1, " +
                        "children: []" +
                    "}, " +
                    "{" +
                        "name: SomeChild2, " +
                        "id: 2, " +
                        "children: [" +
                            "{name: SomeChild2Child1, " +
                            "id: 3, " +
                            "children: []" +
                            "}," +
                            "{name: SomeChild2Child2, " +
                            "id: 4, " +
                            "children: []" +
                            "}," +
                            "{name: SomeChild2Child3, " +
                            "id: 5, " +
                            "children: []}" +
                        "]" +
                    "}, " +
                    "{ " +
                    "name: SomeChild3, " +
                    "id: 6, " +
                    "children: []" +
                    "}" +
                "]" +
                "}";

        SomePojo somePojo = new Main().getPojoFromJson(testjson);
        //System.out.println(somePojo.toString());
        System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(somePojo, SomePojo.class));
    }

    private SomePojo getPojoFromJson(String testjson) {
        GsonBuilder builder = new GsonBuilder();
        Gson gson = builder.registerTypeAdapter(SomePojo.class, new SomePojoDeserializer()).create();
        return gson.fromJson(testjson, SomePojo.class);
    }

}

An example of a very small (and a bit simplified, not all properties) tree would be:

{
  "kind": {
    "num": 294,
    "str": "SourceFile"
  },
  "statements": [
    {
      "kind": {
        "num": 258,
        "str": "ImportDeclaration"
      },
      "importClause": {
        "kind": {
          "num": 259,
          "str": "ImportClause"
        },
        "isTypeOnly": false,
        "namedBindings": {
          "kind": {
            "num": 261,
            "str": "NamedImports"
          },
          "elements": [
            {
              "kind": {
                "num": 262,
                "str": "ImportSpecifier"
              },
              "name": {
                "kind": {
                  "num": 78,
                  "str": "Identifier"
                },
                "escapedText": "UserService"
              }
            }
          ]
        }
      },
      "moduleSpecifier": {
        "kind": {
          "num": 10,
          "str": "StringLiteral"
        },
        "text": "./core",
        "hasExtendedUnicodeEscape": false
      }
    },
    {
      "kind": {
        "num": 249,
        "str": "ClassDeclaration"
      },
      "decorators": [
        {
          "kind": {
            "num": 160,
            "str": "Decorator"
          },
          "expression": {
            "kind": {
              "num": 200,
              "str": "CallExpression"
            },
            "expression": {
              "kind": {
                "num": 78,
                "str": "Identifier"
              },
              "escapedText": "Component"
            },
            "arguments": [
              {
                "kind": {
                  "num": 197,
                  "str": "ObjectLiteralExpression"
                },
                "properties": [
                  {
                    "kind": {
                      "num": 285,
                      "str": "PropertyAssignment"
                    },
                    "name": {
                      "kind": {
                        "num": 78,
                        "str": "Identifier"
                      },
                      "escapedText": "selector"
                    },
                    "initializer": {
                      "kind": {
                        "num": 10,
                        "str": "StringLiteral"
                      },
                      "text": "app-root",
                      "hasExtendedUnicodeEscape": false
                    }
                  },
                  {
                    "kind": {
                      "num": 285,
                      "str": "PropertyAssignment"
                    },
                    "name": {
                      "kind": {
                        "num": 78,
                        "str": "Identifier"
                      },
                      "escapedText": "templateUrl"
                    },
                    "initializer": {
                      "kind": {
                        "num": 10,
                        "str": "StringLiteral"
                      },
                      "text": "./app.component.html",
                      "hasExtendedUnicodeEscape": false
                    }
                  }
                ],
                "multiLine": true
              }
            ]
          }
        }
      ],
      "modifiers": [
        {
          "kind": {
            "num": 92,
            "str": "ExportKeyword"
          }
        }
      ],
      "name": {
        "kind": {
          "num": 78,
          "str": "Identifier"
        },
        "escapedText": "AppComponent"
      },
      "heritageClauses": [
        {
          "kind": {
            "num": 283,
            "str": "HeritageClause"
          },
          "token": 116,
          "types": [
            {
              "kind": {
                "num": 220,
                "str": "ExpressionWithTypeArguments"
              },
              "expression": {
                "kind": {
                  "num": 78,
                  "str": "Identifier"
                },
                "escapedText": "OnInit"
              }
            }
          ]
        }
      ],
      "members": [
        {
          "kind": {
            "num": 165,
            "str": "Constructor"
          },
          "parameters": [
            {
              "kind": {
                "num": 159,
                "str": "Parameter"
              },
              "modifiers": [
                {
                  "kind": {
                    "num": 120,
                    "str": "PrivateKeyword"
                  }
                }
              ],
              "name": {
                "kind": {
                  "num": 78,
                  "str": "Identifier"
                },
                "escapedText": "userService"
              },
              "type": {
                "kind": {
                  "num": 172,
                  "str": "TypeReference"
                },
                "typeName": {
                  "kind": {
                    "num": 78,
                    "str": "Identifier"
                  },
                  "escapedText": "UserService"
                }
              }
            }
          ],
          "body": {
            "kind": {
              "num": 227,
              "str": "Block"
            },
            "statements": [],
            "multiLine": false
          }
        },
        {
          "kind": {
            "num": 164,
            "str": "MethodDeclaration"
          },
          "name": {
            "kind": {
              "num": 78,
              "str": "Identifier"
            },
            "escapedText": "ngOnInit"
          },
          "parameters": [],
          "body": {
            "kind": {
              "num": 227,
              "str": "Block"
            },
            "statements": [
              {
                "kind": {
                  "num": 230,
                  "str": "ExpressionStatement"
                },
                "expression": {
                  "kind": {
                    "num": 200,
                    "str": "CallExpression"
                  },
                  "expression": {
                    "kind": {
                      "num": 198,
                      "str": "PropertyAccessExpression"
                    },
                    "expression": {
                      "kind": {
                        "num": 198,
                        "str": "PropertyAccessExpression"
                      },
                      "expression": {
                        "kind": {
                          "num": 107,
                          "str": "ThisKeyword"
                        }
                      },
                      "name": {
                        "kind": {
                          "num": 78,
                          "str": "Identifier"
                        },
                        "escapedText": "userService"
                      }
                    },
                    "name": {
                      "kind": {
                        "num": 78,
                        "str": "Identifier"
                      },
                      "escapedText": "populate"
                    }
                  },
                  "arguments": []
                }
              }
            ],
            "multiLine": true
          }
        }
      ]
    }
]

Aucun commentaire:

Enregistrer un commentaire