jsonParser.ts 3.5 KB

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