jsonParser.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import { parse } from "jsonc-parser";
  2. const calculateSize = (
  3. text: string | [string, string][],
  4. isParent = false,
  5. isExpanded: boolean
  6. ) => {
  7. let value = "";
  8. if (typeof text === "string") value = text;
  9. else value = text.map(([k, v]) => `${k}: ${v}`).join("\n");
  10. const lineCount = value.split("\n");
  11. const lineLengths = lineCount.map(line => line.length);
  12. const longestLine = lineLengths.sort((a, b) => b - a)[0];
  13. const getWidth = () => {
  14. if (Array.isArray(text) && !text.length) return 40;
  15. if (isExpanded) return 35 + longestLine * 8 + (isParent ? 60 : 0);
  16. if (isParent) return 170;
  17. return 200;
  18. };
  19. const getHeight = () => {
  20. if (lineCount.length * 17.8 < 30) return 40;
  21. return (lineCount.length + 1) * 18;
  22. };
  23. return {
  24. width: getWidth(),
  25. height: getHeight(),
  26. };
  27. };
  28. const filterChild = ([_, v]) => {
  29. const isNull = v === null;
  30. const isArray = Array.isArray(v) && v.length;
  31. const isObject = v instanceof Object;
  32. return !isNull && (isArray || isObject);
  33. };
  34. const filterValues = ([k, v]) => {
  35. if (Array.isArray(v) || v instanceof Object) return false;
  36. return true;
  37. };
  38. function generateChildren(object: Object, isExpanded = true, nextId: () => string) {
  39. if (!(object instanceof Object)) object = [object];
  40. return Object.entries(object)
  41. .filter(filterChild)
  42. .flatMap(([key, v]) => {
  43. const { width, height } = calculateSize(key, true, isExpanded);
  44. const children = extract(v, isExpanded, nextId);
  45. return [
  46. {
  47. id: nextId(),
  48. text: key,
  49. children,
  50. width,
  51. height,
  52. data: {
  53. isParent: true,
  54. hasChild: !!children.length,
  55. },
  56. },
  57. ];
  58. });
  59. }
  60. function generateNodeData(object: Object) {
  61. if (object instanceof Object) {
  62. const entries = Object.entries(object).filter(filterValues);
  63. return entries;
  64. }
  65. return String(object);
  66. }
  67. const extract = (
  68. os: string[] | object[] | null,
  69. isExpanded = true,
  70. nextId = (
  71. id => () =>
  72. String(++id)
  73. )(0)
  74. ) => {
  75. if (!os) return [];
  76. return [os].flat().map(o => {
  77. const text = generateNodeData(o);
  78. const { width, height } = calculateSize(text, false, isExpanded);
  79. return {
  80. id: nextId(),
  81. text,
  82. width,
  83. height,
  84. children: generateChildren(o, isExpanded, nextId),
  85. data: {
  86. isParent: false,
  87. hasChild: false,
  88. isEmpty: !text.length,
  89. },
  90. };
  91. });
  92. };
  93. const flatten = (xs: { id: string; children: never[] }[]) =>
  94. xs.flatMap(({ children, ...rest }) => [rest, ...flatten(children)]);
  95. const relationships = (xs: { id: string; children: never[] }[]) => {
  96. return xs.flatMap(({ id: from, children = [] }) => [
  97. ...children.map(({ id: to }) => ({
  98. id: `e${from}-${to}`,
  99. from,
  100. to,
  101. })),
  102. ...relationships(children),
  103. ]);
  104. };
  105. export const parser = (jsonStr: string, isExpanded = true) => {
  106. try {
  107. let json = parse(jsonStr);
  108. if (!Array.isArray(json)) json = [json];
  109. const nodes: NodeData[] = [];
  110. const edges: EdgeData[] = [];
  111. const mappedElements = extract(json, isExpanded);
  112. const res = [...flatten(mappedElements), ...relationships(mappedElements)];
  113. res.forEach(data => {
  114. if (isNode(data)) {
  115. nodes.push(data);
  116. } else {
  117. edges.push(data);
  118. }
  119. });
  120. return { nodes, edges };
  121. } catch (error) {
  122. console.error(error);
  123. return {
  124. nodes: [],
  125. edges: [],
  126. };
  127. }
  128. };
  129. function isNode(element: NodeData | EdgeData) {
  130. if ("text" in element) return true;
  131. return false;
  132. }