jsonParser.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import toast from "react-hot-toast";
  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 (isExpanded) return 35 + longestLine * 8 + (isParent ? 60 : 0);
  15. if (isParent) return 150;
  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. },
  88. };
  89. });
  90. };
  91. const flatten = (xs: { id: string; children: never[] }[]) =>
  92. xs.flatMap(({ children, ...rest }) => [rest, ...flatten(children)]);
  93. const relationships = (xs: { id: string; children: never[] }[]) => {
  94. return xs.flatMap(({ id: from, children = [] }) => [
  95. ...children.map(({ id: to }) => ({
  96. id: `e${from}-${to}`,
  97. from,
  98. to,
  99. })),
  100. ...relationships(children),
  101. ]);
  102. };
  103. export const parser = (jsonStr: string, isExpanded = true) => {
  104. try {
  105. let json = JSON.parse(jsonStr);
  106. if (!Array.isArray(json)) json = [json];
  107. const nodes: NodeData[] = [];
  108. const edges: EdgeData[] = [];
  109. const mappedElements = extract(json, isExpanded);
  110. const res = [...flatten(mappedElements), ...relationships(mappedElements)];
  111. res.forEach(data => {
  112. if (isNode(data)) {
  113. nodes.push(data);
  114. } else {
  115. edges.push(data);
  116. }
  117. });
  118. return { nodes, edges };
  119. } catch (error) {
  120. console.error(error);
  121. toast.error("An error occured while parsing JSON data!");
  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. }