|
@@ -1,4 +1,4 @@
|
|
|
-import { parse } from "jsonc-parser";
|
|
|
+import { Node, parseTree } from "jsonc-parser";
|
|
|
|
|
|
const calculateSize = (
|
|
|
text: string | [string, string][],
|
|
@@ -32,113 +32,303 @@ const calculateSize = (
|
|
|
};
|
|
|
};
|
|
|
|
|
|
-const filterChild = ([_, v]) => {
|
|
|
- const isNull = v === null;
|
|
|
- const isArray = Array.isArray(v) && v.length;
|
|
|
- const isObject = v instanceof Object;
|
|
|
-
|
|
|
- return !isNull && (isArray || isObject);
|
|
|
-};
|
|
|
-
|
|
|
-const filterValues = ([k, v]) => {
|
|
|
- if (Array.isArray(v) || v instanceof Object) return false;
|
|
|
- return true;
|
|
|
-};
|
|
|
-
|
|
|
-function generateChildren(object: Object, isFolded = false, nextId: () => string) {
|
|
|
- if (!(object instanceof Object)) object = [object];
|
|
|
-
|
|
|
- return Object.entries(object)
|
|
|
- .filter(filterChild)
|
|
|
- .flatMap(([key, v]) => {
|
|
|
- const { width, height } = calculateSize(key, true, isFolded);
|
|
|
- const children = extract(v, isFolded, nextId);
|
|
|
+export const parser = (jsonStr: string, isFolded = false) => {
|
|
|
+ try {
|
|
|
+ let json = parseTree(jsonStr);
|
|
|
+ let nodes: NodeData[] = [];
|
|
|
+ let edges: EdgeData[] = [];
|
|
|
|
|
|
- return [
|
|
|
+ const addNodes = (
|
|
|
+ text: any,
|
|
|
+ width: number,
|
|
|
+ height: number,
|
|
|
+ parent: boolean,
|
|
|
+ isEmpty?: boolean
|
|
|
+ ) => {
|
|
|
+ let actualId = String(nodes.length + 1);
|
|
|
+ nodes = [
|
|
|
+ ...nodes,
|
|
|
{
|
|
|
- id: nextId(),
|
|
|
- text: key,
|
|
|
- children,
|
|
|
- width,
|
|
|
- height,
|
|
|
+ id: actualId,
|
|
|
+ text: text,
|
|
|
+ width: width,
|
|
|
+ height: height,
|
|
|
data: {
|
|
|
- isParent: true,
|
|
|
- childrenCount: children.length,
|
|
|
+ isParent: parent,
|
|
|
+ childrenCount: parent ? 1 : 0,
|
|
|
+ isEmpty: isEmpty,
|
|
|
},
|
|
|
},
|
|
|
];
|
|
|
- });
|
|
|
-}
|
|
|
+ return actualId;
|
|
|
+ };
|
|
|
|
|
|
-function generateNodeData(object: Object) {
|
|
|
- if (object instanceof Object) {
|
|
|
- const entries = Object.entries(object).filter(filterValues);
|
|
|
- return entries;
|
|
|
- }
|
|
|
+ const addEdges = (from: string, to: string) => {
|
|
|
+ edges = [
|
|
|
+ ...edges,
|
|
|
+ {
|
|
|
+ id: `e${from}-${to}`,
|
|
|
+ from: from,
|
|
|
+ to: to,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ };
|
|
|
|
|
|
- return String(object);
|
|
|
-}
|
|
|
+ let parentName: string = "";
|
|
|
+ let bracketOpen: { id: string; type: string }[] = [];
|
|
|
+ let objectsFromArray: number[] = [];
|
|
|
+ let objectsFromArrayId = 0;
|
|
|
+ let notHaveParent: string[] = [];
|
|
|
+ let brothersNode: [string, string][] = [];
|
|
|
+ let brothersParentId: string | undefined = "";
|
|
|
+ let brotherKey: string = "";
|
|
|
+ let brothersNodeProps: {
|
|
|
+ id: string;
|
|
|
+ parentId: string | undefined;
|
|
|
+ objectsFromArrayId: number | undefined;
|
|
|
+ }[] = [];
|
|
|
|
|
|
-const extract = (
|
|
|
- os: string[] | object[] | null,
|
|
|
- isFolded = false,
|
|
|
- nextId = (
|
|
|
- id => () =>
|
|
|
- String(++id)
|
|
|
- )(0)
|
|
|
-) => {
|
|
|
- if (!os) return [];
|
|
|
+ const traverse = (
|
|
|
+ objectToTraverse: Node,
|
|
|
+ parentType?: string,
|
|
|
+ myParentId?: string,
|
|
|
+ nextType?: string
|
|
|
+ ) => {
|
|
|
+ let { type, children, value } = objectToTraverse;
|
|
|
|
|
|
- return [os].flat().map(o => {
|
|
|
- const text = generateNodeData(o);
|
|
|
- const { width, height } = calculateSize(text, false, isFolded);
|
|
|
+ if (!children) {
|
|
|
+ if (value !== undefined) {
|
|
|
+ if (
|
|
|
+ parentType === "property" &&
|
|
|
+ nextType !== "object" &&
|
|
|
+ nextType !== "array"
|
|
|
+ ) {
|
|
|
+ brothersParentId = myParentId;
|
|
|
+ if (nextType === undefined) {
|
|
|
+ // add key and value to brothers node
|
|
|
+ brothersNode = [...brothersNode, [brotherKey, value]];
|
|
|
+ } else {
|
|
|
+ brotherKey = value;
|
|
|
+ }
|
|
|
+ } else if (parentType === "array") {
|
|
|
+ const { width, height } = calculateSize(String(value), false, isFolded);
|
|
|
+ const nodeFromArrayId = addNodes(String(value), width, height, false);
|
|
|
+ if (myParentId) {
|
|
|
+ addEdges(myParentId, nodeFromArrayId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (nextType && parentType !== "array") {
|
|
|
+ if (nextType === "object" || nextType === "array") {
|
|
|
+ parentName = value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (children) {
|
|
|
+ let parentId: string | undefined;
|
|
|
|
|
|
- return {
|
|
|
- id: nextId(),
|
|
|
- text,
|
|
|
- width,
|
|
|
- height,
|
|
|
- children: generateChildren(o, isFolded, nextId),
|
|
|
- data: {
|
|
|
- isParent: false,
|
|
|
- childrenCount: 0,
|
|
|
- isEmpty: !text.length,
|
|
|
- },
|
|
|
- };
|
|
|
- });
|
|
|
-};
|
|
|
+ if (type !== "property" && parentName !== "") {
|
|
|
+ // add last brothers node and add parent node
|
|
|
+
|
|
|
+ if (brothersNode.length > 0) {
|
|
|
+ // add or concat brothers node of same parent
|
|
|
+ let findBrothersNode = brothersNodeProps.find(
|
|
|
+ e =>
|
|
|
+ e.parentId === brothersParentId &&
|
|
|
+ e.objectsFromArrayId ===
|
|
|
+ objectsFromArray[objectsFromArray.length - 1]
|
|
|
+ );
|
|
|
+ if (findBrothersNode) {
|
|
|
+ let ModifyNodes = [...nodes];
|
|
|
+ let findNode = nodes.findIndex(e => e.id === findBrothersNode?.id);
|
|
|
|
|
|
-const flatten = (xs: { id: string; children: never[] }[]) =>
|
|
|
- xs.flatMap(({ children, ...rest }) => [rest, ...flatten(children)]);
|
|
|
-
|
|
|
-const relationships = (xs: { id: string; children: never[] }[]) => {
|
|
|
- return xs.flatMap(({ id: from, children = [] }) => [
|
|
|
- ...children.map(({ id: to }) => ({
|
|
|
- id: `e${from}-${to}`,
|
|
|
- from,
|
|
|
- to,
|
|
|
- })),
|
|
|
- ...relationships(children),
|
|
|
- ]);
|
|
|
-};
|
|
|
+ if (ModifyNodes[findNode]) {
|
|
|
+ ModifyNodes[findNode].text =
|
|
|
+ ModifyNodes[findNode].text.concat(brothersNode);
|
|
|
+ const { width, height } = calculateSize(
|
|
|
+ ModifyNodes[findNode].text,
|
|
|
+ false,
|
|
|
+ isFolded
|
|
|
+ );
|
|
|
+ ModifyNodes[findNode].width = width;
|
|
|
+ ModifyNodes[findNode].height = height;
|
|
|
+ nodes = [...ModifyNodes];
|
|
|
+ brothersNode = [];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const { width, height } = calculateSize(brothersNode, false, isFolded);
|
|
|
+ const brothersNodeId = addNodes(brothersNode, width, height, false);
|
|
|
+ brothersNode = [];
|
|
|
|
|
|
-export const parser = (jsonStr: string, isFolded = false) => {
|
|
|
- try {
|
|
|
- let json = parse(jsonStr);
|
|
|
- if (!Array.isArray(json)) json = [json];
|
|
|
- const nodes: NodeData[] = [];
|
|
|
- const edges: EdgeData[] = [];
|
|
|
-
|
|
|
- const mappedElements = extract(json, isFolded);
|
|
|
- const res = [...flatten(mappedElements), ...relationships(mappedElements)];
|
|
|
-
|
|
|
- res.forEach(data => {
|
|
|
- if (isNode(data)) {
|
|
|
- nodes.push(data);
|
|
|
- } else {
|
|
|
- edges.push(data);
|
|
|
+ if (brothersParentId) {
|
|
|
+ addEdges(brothersParentId, brothersNodeId);
|
|
|
+ } else {
|
|
|
+ notHaveParent = [...notHaveParent, brothersNodeId];
|
|
|
+ }
|
|
|
+
|
|
|
+ brothersNodeProps = [
|
|
|
+ ...brothersNodeProps,
|
|
|
+ {
|
|
|
+ id: brothersNodeId,
|
|
|
+ parentId: brothersParentId,
|
|
|
+ objectsFromArrayId: objectsFromArray[objectsFromArray.length - 1],
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // add parent node
|
|
|
+ const { width, height } = calculateSize(parentName, true, isFolded);
|
|
|
+ parentId = addNodes(parentName, width, height, true);
|
|
|
+ bracketOpen = [...bracketOpen, { id: parentId, type: type }];
|
|
|
+ parentName = "";
|
|
|
+
|
|
|
+ // add edges from parent node
|
|
|
+ let brothersProps = brothersNodeProps.filter(
|
|
|
+ e =>
|
|
|
+ e.parentId === myParentId &&
|
|
|
+ e.objectsFromArrayId === objectsFromArray[objectsFromArray.length - 1]
|
|
|
+ );
|
|
|
+ if (
|
|
|
+ (brothersProps.length > 0 &&
|
|
|
+ bracketOpen[bracketOpen.length - 2] &&
|
|
|
+ bracketOpen[bracketOpen.length - 2].type !== "object") ||
|
|
|
+ (brothersProps.length > 0 && bracketOpen.length === 1)
|
|
|
+ ) {
|
|
|
+ addEdges(brothersProps[brothersProps.length - 1].id, parentId);
|
|
|
+ } else if (myParentId) {
|
|
|
+ addEdges(myParentId, parentId);
|
|
|
+ } else {
|
|
|
+ notHaveParent = [...notHaveParent, parentId];
|
|
|
+ }
|
|
|
+ } else if (parentType === "array") {
|
|
|
+ objectsFromArray = [...objectsFromArray, objectsFromArrayId++];
|
|
|
+ }
|
|
|
+ children.forEach((branch, index, array) => {
|
|
|
+ if (array[index + 1]) {
|
|
|
+ traverse(
|
|
|
+ branch,
|
|
|
+ type,
|
|
|
+ bracketOpen[bracketOpen.length - 1]
|
|
|
+ ? bracketOpen[bracketOpen.length - 1].id
|
|
|
+ : undefined,
|
|
|
+ array[index + 1].type
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ traverse(
|
|
|
+ branch,
|
|
|
+ type,
|
|
|
+ bracketOpen[bracketOpen.length - 1]
|
|
|
+ ? bracketOpen[bracketOpen.length - 1].id
|
|
|
+ : undefined
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (type !== "property") {
|
|
|
+ // when children end
|
|
|
+
|
|
|
+ // add or concat brothers node when it is the last parent node
|
|
|
+ if (brothersNode.length > 0) {
|
|
|
+ let findBrothersNode = brothersNodeProps.find(
|
|
|
+ e =>
|
|
|
+ e.parentId === brothersParentId &&
|
|
|
+ e.objectsFromArrayId ===
|
|
|
+ objectsFromArray[objectsFromArray.length - 1]
|
|
|
+ );
|
|
|
+ if (findBrothersNode) {
|
|
|
+ let ModifyNodes = [...nodes];
|
|
|
+ let findNode = nodes.findIndex(e => e.id === findBrothersNode?.id);
|
|
|
+
|
|
|
+ if (ModifyNodes[findNode]) {
|
|
|
+ ModifyNodes[findNode].text =
|
|
|
+ ModifyNodes[findNode].text.concat(brothersNode);
|
|
|
+ const { width, height } = calculateSize(
|
|
|
+ ModifyNodes[findNode].text,
|
|
|
+ false,
|
|
|
+ isFolded
|
|
|
+ );
|
|
|
+ ModifyNodes[findNode].width = width;
|
|
|
+ ModifyNodes[findNode].height = height;
|
|
|
+ nodes = [...ModifyNodes];
|
|
|
+ brothersNode = [];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const { width, height } = calculateSize(brothersNode, false, isFolded);
|
|
|
+ const brothersNodeId = addNodes(brothersNode, width, height, false);
|
|
|
+ brothersNode = [];
|
|
|
+
|
|
|
+ if (brothersParentId) {
|
|
|
+ addEdges(brothersParentId, brothersNodeId);
|
|
|
+ } else {
|
|
|
+ notHaveParent = [...notHaveParent, brothersNodeId];
|
|
|
+ }
|
|
|
+
|
|
|
+ brothersNodeProps = [
|
|
|
+ ...brothersNodeProps,
|
|
|
+ {
|
|
|
+ id: brothersNodeId,
|
|
|
+ parentId: brothersParentId,
|
|
|
+ objectsFromArrayId: objectsFromArray[objectsFromArray.length - 1],
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // close brackets
|
|
|
+ if (parentType !== "array") {
|
|
|
+ if (bracketOpen.length > 0) {
|
|
|
+ let newBracketOpen = [...bracketOpen];
|
|
|
+ newBracketOpen.splice(newBracketOpen.length - 1);
|
|
|
+ bracketOpen = [...newBracketOpen];
|
|
|
+ }
|
|
|
+ } else if (parentType === "array") {
|
|
|
+ if (objectsFromArray.length > 0) {
|
|
|
+ let newobjectsFromArray = [...objectsFromArray];
|
|
|
+ newobjectsFromArray.splice(newobjectsFromArray.length - 1);
|
|
|
+ objectsFromArray = [...newobjectsFromArray];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (parentId) {
|
|
|
+ let myChildrens = edges.filter(e => e.from === parentId);
|
|
|
+ let myIndex = nodes.findIndex(e => e.id === parentId);
|
|
|
+
|
|
|
+ let ModifyNodes = [...nodes];
|
|
|
+ if (ModifyNodes[myIndex]) {
|
|
|
+ ModifyNodes[myIndex].data.childrenCount = myChildrens.length;
|
|
|
+ nodes = [...ModifyNodes];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- });
|
|
|
+ };
|
|
|
+
|
|
|
+ if (json) {
|
|
|
+ traverse(json);
|
|
|
+
|
|
|
+ if (notHaveParent.length > 1) {
|
|
|
+ if (json.type !== "array") {
|
|
|
+ const text = "";
|
|
|
+ const { width, height } = calculateSize(text, false, isFolded);
|
|
|
+ const emptyId = addNodes(text, width, height, false, true);
|
|
|
+ notHaveParent.forEach(children => {
|
|
|
+ addEdges(emptyId, children);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nodes.length === 0) {
|
|
|
+ if (json.type === "array") {
|
|
|
+ const text = "[]";
|
|
|
+ const { width, height } = calculateSize(text, false, isFolded);
|
|
|
+ addNodes(text, width, height, false);
|
|
|
+ } else {
|
|
|
+ const text = "{}";
|
|
|
+ const { width, height } = calculateSize(text, false, isFolded);
|
|
|
+ addNodes(text, width, height, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
return { nodes, edges };
|
|
|
} catch (error) {
|
|
@@ -149,8 +339,3 @@ export const parser = (jsonStr: string, isFolded = false) => {
|
|
|
};
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
-function isNode(element: NodeData | EdgeData) {
|
|
|
- if ("text" in element) return true;
|
|
|
- return false;
|
|
|
-}
|