Skip to content

Commit 8f7c6fd

Browse files
committed
fix: data format conversion
1 parent 12a66ea commit 8f7c6fd

File tree

4 files changed

+107
-59
lines changed

4 files changed

+107
-59
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"allotment": "^1.20.3",
3131
"axios": "^1.8.3",
3232
"dayjs": "^1.11.13",
33+
"fast-xml-parser": "^5.2.1",
3334
"gofmt.js": "^0.0.2",
3435
"html-to-image": "^1.11.13",
3536
"jq-web": "^0.5.1",

pnpm-lock.yaml

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/features/editor/views/GraphView/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from "react";
22
import { LoadingOverlay, useComputedColorScheme } from "@mantine/core";
3+
import { useDebouncedValue } from "@mantine/hooks";
34
import styled from "styled-components";
45
import debounce from "lodash.debounce";
56
import { Space } from "react-zoomable-ui";
@@ -144,6 +145,7 @@ export const GraphView = ({ isWidget = false }: GraphProps) => {
144145
const loading = useGraph(state => state.loading);
145146
const gesturesEnabled = useConfig(state => state.gesturesEnabled);
146147
const rulersEnabled = useConfig(state => state.rulersEnabled);
148+
const [debouncedLoading] = useDebouncedValue(loading, 300);
147149

148150
const callback = React.useCallback(() => {
149151
const canvas = document.querySelector(".jsoncrack-canvas") as HTMLDivElement | null;
@@ -172,7 +174,7 @@ export const GraphView = ({ isWidget = false }: GraphProps) => {
172174

173175
return (
174176
<>
175-
<LoadingOverlay visible={loading} />
177+
<LoadingOverlay visible={debouncedLoading} />
176178
{!isWidget && <OptionsMenu />}
177179
{!isWidget && <SecureInfo />}
178180
<ZoomControl isWidget={isWidget} />

src/lib/utils/jsonAdapter.ts

Lines changed: 87 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,104 @@
1+
import type { ParseError } from "jsonc-parser";
12
import { FileFormat } from "../../enums/file.enum";
23

3-
const keyExists = (obj: object, key: string) => {
4-
if (!obj || (typeof obj !== "object" && !Array.isArray(obj))) {
5-
return false;
6-
} else if (obj.hasOwnProperty(key)) {
7-
return obj[key];
8-
} else if (Array.isArray(obj)) {
9-
for (let i = 0; i < obj.length; i++) {
10-
const result = keyExists(obj[i], key);
4+
export const contentToJson = (value: string, format = FileFormat.JSON): Promise<object> => {
5+
return new Promise(async (resolve, reject) => {
6+
try {
7+
if (!value) return resolve({});
118

12-
if (result) {
13-
return result;
9+
if (format === FileFormat.JSON) {
10+
const { parse } = await import("jsonc-parser");
11+
const errors: ParseError[] = [];
12+
const result = parse(value, errors);
13+
if (errors.length > 0) JSON.parse(value);
14+
return resolve(result);
1415
}
15-
}
16-
} else {
17-
for (const k in obj) {
18-
const result = keyExists(obj[k], key);
1916

20-
if (result) {
21-
return result;
17+
if (format === FileFormat.YAML) {
18+
const { load } = await import("js-yaml");
19+
return resolve(load(value) as object);
2220
}
23-
}
24-
}
2521

26-
return false;
27-
};
22+
if (format === FileFormat.XML) {
23+
const { XMLParser } = await import("fast-xml-parser");
24+
const parser = new XMLParser({
25+
attributeNamePrefix: "$",
26+
ignoreAttributes: false,
27+
allowBooleanAttributes: true,
28+
parseAttributeValue: true,
29+
trimValues: true,
30+
parseTagValue: true,
31+
});
32+
return resolve(parser.parse(value));
33+
}
2834

29-
const contentToJson = async (value: string, format = FileFormat.JSON): Promise<object> => {
30-
try {
31-
const { load } = await import("js-yaml");
32-
const { csv2json } = await import("json-2-csv");
33-
const { parse } = await import("jsonc-parser");
34-
const jxon = await import("jxon");
35-
const toml = await import("toml");
35+
if (format === FileFormat.CSV) {
36+
const { csv2json } = await import("json-2-csv");
37+
const result = csv2json(value, {
38+
trimFieldValues: true,
39+
trimHeaderFields: true,
40+
wrapBooleans: true,
41+
excelBOM: true,
42+
});
43+
return resolve(result);
44+
}
3645

37-
let json: object = {};
46+
return resolve({});
47+
} catch (error) {
48+
const errorMessage = error instanceof Error ? error.message : "Failed to parse content";
49+
return reject(errorMessage);
50+
}
51+
});
52+
};
3853

39-
if (format === FileFormat.JSON) json = parse(value);
40-
if (format === FileFormat.YAML) json = load(value) as object;
41-
if (format === FileFormat.XML) json = jxon.stringToJs(value);
42-
if (format === FileFormat.TOML) json = toml.parse(value);
43-
if (format === FileFormat.CSV) json = await csv2json(value);
44-
if (format === FileFormat.XML && keyExists(json, "parsererror")) throw Error("Unknown error!");
54+
export const jsonToContent = async (json: string, format: FileFormat): Promise<string> => {
55+
return new Promise(async resolve => {
56+
try {
57+
if (!json) return resolve("");
4558

46-
if (!json) throw Error("Invalid JSON!");
59+
if (format === FileFormat.JSON) {
60+
const parsedJson = JSON.parse(json);
61+
return resolve(JSON.stringify(parsedJson, null, 2));
62+
}
4763

48-
return Promise.resolve(json);
49-
} catch (error: any) {
50-
throw error;
51-
}
52-
};
64+
if (format === FileFormat.YAML) {
65+
const { dump } = await import("js-yaml");
66+
const { parse } = await import("jsonc-parser");
67+
return resolve(dump(parse(json)));
68+
}
5369

54-
const jsonToContent = async (json: string, format: FileFormat): Promise<string> => {
55-
try {
56-
const { dump } = await import("js-yaml");
57-
const { json2csv } = await import("json-2-csv");
58-
const { parse } = await import("jsonc-parser");
70+
if (format === FileFormat.XML) {
71+
const { XMLBuilder } = await import("fast-xml-parser");
72+
const builder = new XMLBuilder({
73+
format: true,
74+
attributeNamePrefix: "$",
75+
ignoreAttributes: false,
76+
});
5977

60-
let contents = json;
78+
return resolve(builder.build(JSON.parse(json)));
79+
}
6180

62-
if (!json) return json;
63-
if (format === FileFormat.JSON) contents = json;
64-
if (format === FileFormat.YAML) contents = dump(parse(json));
65-
if (format === FileFormat.XML) contents = dump(parse(json));
66-
if (format === FileFormat.TOML) contents = dump(parse(json));
67-
if (format === FileFormat.CSV) contents = await json2csv(parse(json));
81+
if (format === FileFormat.CSV) {
82+
const { json2csv } = await import("json-2-csv");
83+
const parsedJson = JSON.parse(json);
6884

69-
return Promise.resolve(contents);
70-
} catch (error: any) {
71-
throw error;
72-
}
73-
};
85+
const data = Array.isArray(parsedJson) ? parsedJson : [parsedJson];
86+
return resolve(
87+
json2csv(data, {
88+
expandArrayObjects: true,
89+
expandNestedObjects: true,
90+
excelBOM: true,
91+
wrapBooleans: true,
92+
trimFieldValues: true,
93+
trimHeaderFields: true,
94+
})
95+
);
96+
}
7497

75-
export { contentToJson, jsonToContent };
98+
return resolve(json);
99+
} catch (error) {
100+
console.error(json, error);
101+
return resolve(json);
102+
}
103+
});
104+
};

0 commit comments

Comments
 (0)