Skip to content

Commit b029d41

Browse files
Fix issue #12: Improve error handling for malformed JSON
Co-authored-by: CNSeniorious000 <[email protected]>
1 parent 035a62a commit b029d41

File tree

2 files changed

+76
-5
lines changed

2 files changed

+76
-5
lines changed

src/index.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,13 @@ const _parseJSON = (jsonString: string, allow: number) => {
6565
index += 3;
6666
return NaN;
6767
}
68-
return parseNum();
68+
// Check if we have a valid number character before calling parseNum
69+
const char = jsonString[index];
70+
if (char === "-" || (char >= "0" && char <= "9")) {
71+
return parseNum();
72+
}
73+
// If we get here, it's an invalid token
74+
throwMalformedError(`Unexpected token '${char}'`);
6975
};
7076

7177
const parseStr: () => string = () => {
@@ -108,13 +114,21 @@ const _parseJSON = (jsonString: string, allow: number) => {
108114
const value = parseAny();
109115
obj[key] = value;
110116
} catch (e) {
117+
// If it's a malformed JSON error, let it bubble up
118+
if (e instanceof MalformedJSON) {
119+
throw e;
120+
}
111121
if (Allow.OBJ & allow) return obj;
112122
else throw e;
113123
}
114124
skipBlank();
115125
if (jsonString[index] === ",") index++; // skip comma
116126
}
117127
} catch (e) {
128+
// If it's a malformed JSON error, let it bubble up
129+
if (e instanceof MalformedJSON) {
130+
throw e;
131+
}
118132
if (Allow.OBJ & allow) return obj;
119133
else markPartialJSON("Expected '}' at end of object");
120134
}
@@ -124,16 +138,22 @@ const _parseJSON = (jsonString: string, allow: number) => {
124138

125139
const parseArr = () => {
126140
index++; // skip initial bracket
141+
skipBlank(); // skip whitespace at start of array
127142
const arr = [];
128143
try {
129144
while (jsonString[index] !== "]") {
130145
arr.push(parseAny());
131146
skipBlank();
132147
if (jsonString[index] === ",") {
133148
index++; // skip comma
149+
skipBlank(); // skip whitespace after comma
134150
}
135151
}
136152
} catch (e) {
153+
// If it's a malformed JSON error, let it bubble up
154+
if (e instanceof MalformedJSON) {
155+
throw e;
156+
}
137157
if (Allow.ARR & allow) {
138158
return arr;
139159
}
@@ -168,11 +188,19 @@ const _parseJSON = (jsonString: string, allow: number) => {
168188
return JSON.parse(jsonString.substring(start, index));
169189
} catch (e) {
170190
if (jsonString.substring(start, index) === "-") markPartialJSON("Not sure what '-' is");
171-
try {
172-
return JSON.parse(jsonString.substring(start, jsonString.lastIndexOf("e")));
173-
} catch (e) {
174-
throwMalformedError(String(e));
191+
// If the number is partial and we allow partial numbers, try to parse up to last 'e'
192+
if (Allow.NUM & allow) {
193+
const numberStr = jsonString.substring(start, index);
194+
const lastE = numberStr.lastIndexOf("e");
195+
if (lastE > 0) {
196+
try {
197+
return JSON.parse(numberStr.substring(0, lastE));
198+
} catch (e2) {
199+
// Still invalid, fall through to error
200+
}
201+
}
175202
}
203+
throwMalformedError(String(e));
176204
}
177205
};
178206

tests/issue12.test.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { parse, MalformedJSON } from "../src/index";
2+
import { test, expect } from "vitest";
3+
4+
test("issue #12 - invalid number starting with dot should throw error", () => {
5+
// This should throw an error instead of silently failing
6+
expect(() => parse(`{
7+
"vector": [1, 2, 3, .0516156161551515, 7]
8+
}`)).toThrow(MalformedJSON);
9+
10+
// Standalone invalid numbers should also throw
11+
expect(() => parse(".123")).toThrow(MalformedJSON);
12+
expect(() => parse("[1, .123, 3]")).toThrow(MalformedJSON);
13+
});
14+
15+
test("issue #12 - invalid tokens should throw error instead of returning empty", () => {
16+
// Should throw error instead of returning []
17+
expect(() => parse("[abc")).toThrow(MalformedJSON);
18+
expect(() => parse("[invalid")).toThrow(MalformedJSON);
19+
});
20+
21+
test("issue #12 - empty array with spaces should not stop parsing", () => {
22+
// This should parse the complete JSON, not stop at the empty array
23+
const input = `[
24+
{ "id":1,"arr":["hello"]},
25+
{"id":2, "arr": [ ],"more":"yaya"},
26+
{"id":3,"arr":["!"]}
27+
]`;
28+
29+
const result = parse(input);
30+
expect(result).toHaveLength(3);
31+
expect(result[0]).toEqual({ "id": 1, "arr": ["hello"] });
32+
expect(result[1]).toEqual({ "id": 2, "arr": [], "more": "yaya" });
33+
expect(result[2]).toEqual({ "id": 3, "arr": ["!"] });
34+
});
35+
36+
test("valid edge cases should still work", () => {
37+
// These should continue to work as before
38+
expect(parse("[]")).toEqual([]);
39+
expect(parse("[ ]")).toEqual([]);
40+
expect(parse("[1, 2, 3]")).toEqual([1, 2, 3]);
41+
expect(parse("0.123")).toBe(0.123);
42+
expect(parse("[0.123]")).toEqual([0.123]);
43+
});

0 commit comments

Comments
 (0)