A modular, extensible JSON parser and serializer built from scratch in TypeScript for deep learning and exploration of data serialization concepts.
This project implements a complete JSON parser and serializer with support for extended data types beyond standard JSON.
- Full JSON Compliance - Supports all standard JSON types (string, number, boolean, null, object, array)
- Custom Parser - Hand-written tokenizer and recursive descent parser
- Custom Serializer - Configurable stringification with formatting options
- Error Handling - Detailed error messages with position tracking
- π Date Objects - Automatic ISO 8601 date parsing and serialization
- πΊοΈ Map Support - Serialize and deserialize ES6 Map objects with any key types
- π― Set Support - Full Set object support with nested structures
- π¬ Comments - Single-line (
//) and multi-line (/* */) comment support - π· Undefined - Optional undefined value serialization
- π Circular Detection - Prevents infinite loops with circular reference detection
- Reviver Function - Transform values during parsing (like
JSON.parse) - Replacer Function - Filter/transform values during serialization (like
JSON.stringify) - Trailing Commas - Allowed when extensions are enabled
- Custom Extensions - Plugin architecture for adding your own types
# Clone the repository
git clone https://github.com/abubalo/extended-json-parser.git
cd extended-json-parser
# Install dependencies
npm installimport { _JSON } from './src/index';
// Simple parsing
const data = _JSON.parse('{"name": "Alice", "age": 30}');
console.log(data); // { name: 'Alice', age: 30 }
// Simple stringification
const json = _JSON.stringify({ name: "Bob", active: true });
console.log(json); // {"name":"Bob","active":true}const obj = {
person: {
name: "Alice",
age: 30,
skills: ["JavaScript", "Python", "TypeScript"]
}
};
const formatted = _JSON.stringify(obj, { space: 2 });
console.log(formatted);
// {
// "person": {
// "name": "Alice",
// "age": 30,
// "skills": [
// "JavaScript",
// "Python",
// "TypeScript"
// ]
// }
// }const event = {
name: "Meeting",
date: new Date("2025-10-31T10:00:00.000Z")
};
// Serialize with date support
const json = _JSON.stringify(event, {
extensions: { dates: true },
space: 2
});
// Parse with automatic date conversion
const parsed = _JSON.parse(json, {
extensions: { dates: true }
});
console.log(parsed.date instanceof Date); // true// Map example
const userMap = new Map([
["alice", { role: "admin", active: true }],
["bob", { role: "user", active: false }]
]);
const json = _JSON.stringify(
{ users: userMap },
{ extensions: { maps: true }, space: 2 }
);
const parsed = _JSON.parse(json, { extensions: { maps: true } });
console.log(parsed.users instanceof Map); // true
console.log(parsed.users.get("alice")); // { role: 'admin', active: true }
// Set example
const tags = new Set(["javascript", "typescript", "nodejs"]);
const json2 = _JSON.stringify(
{ tags },
{ extensions: { sets: true } }
);
const parsed2 = _JSON.parse(json2, { extensions: { sets: true } });
console.log(parsed2.tags instanceof Set); // trueconst jsonWithComments = `{
// User configuration
"name": "Alice",
/*
* Multi-line comment
* explaining the age field
*/
"age": 30
}`;
const data = _JSON.parse(jsonWithComments, {
extensions: { comments: true }
});
console.log(data); // { name: 'Alice', age: 30 }// Reviver - transform during parsing
const json = '{"price": "100", "tax": "15"}';
const data = _JSON.parse(json, {
reviver: (key, value) => {
if (key === "price" || key === "tax") {
return parseInt(value);
}
return value;
}
});
console.log(data); // { price: 100, tax: 15 }
// Replacer - filter during serialization
const obj = {
name: "Alice",
password: "secret123",
age: 30
};
const safe = _JSON.stringify(obj, {
replacer: (key, value) => {
if (key === "password") return undefined;
return value;
},
space: 2
});
// Output won't include password fieldconst complex = {
users: new Map([
["alice", new Set(["admin", "editor"])],
["bob", new Set(["viewer"])]
]),
metadata: {
created: new Date("2025-01-15T08:00:00.000Z"),
updated: new Date("2025-10-31T12:00:00.000Z")
}
};
const json = _JSON.stringify(complex, {
extensions: {
maps: true,
sets: true,
dates: true
},
space: 2
});
const parsed = _JSON.parse(json, {
extensions: {
maps: true,
sets: true,
dates: true
}
});
// All types are preserved
console.log(parsed.users instanceof Map); // true
console.log(parsed.users.get("alice") instanceof Set); // true
console.log(parsed.metadata.created instanceof Date); // trueParses a JSON string into a JavaScript value.
Parameters:
text(string) - The JSON string to parseoptions(ParserOptions) - Optional configurationreviver- Function to transform values during parsingextensions- Enable extended featuresdates(boolean) - Auto-parse ISO date stringscomments(boolean) - Allow comments in JSONundefined(boolean) - Support undefined valuesmaps(boolean) - Deserialize Map objectssets(boolean) - Deserialize Set objects
Returns: Parsed JavaScript value
Throws: JSONParseError if parsing fails
Converts a JavaScript value to a JSON string.
Parameters:
value(JsonValue) - The value to serializeoptions(StringifyOptions) - Optional configurationreplacer- Function to filter/transform valuesspace(string | number) - Indentation for pretty-printingextensions- Enable extended featuresdates(boolean) - Serialize Date objectsundefined(boolean) - Include undefined valuesmaps(boolean) - Serialize Map objectssets(boolean) - Serialize Set objects
Returns: JSON string
Throws: TypeError for circular references or unsupported types
This project was built to understand:
- β JSON specification and format structure
- β Lexical analysis and tokenization
- β Recursive descent parsing techniques
- β Abstract Syntax Tree (AST) concepts
- β Serialization and deserialization patterns
- Error handling and recovery strategies
- Circular reference detection
- Extension architecture and plugins
- Type preservation across serialization
- Modular software design patterns
| Type | Parse | Stringify | Notes |
|---|---|---|---|
| String | β | β | Full escape sequence support |
| Number | β | β | IEEE 754 double precision |
| Boolean | β | β | true / false |
| Null | β | β | null |
| Object | β | β | Nested objects supported |
| Array | β | β | Mixed-type arrays supported |
| Type | Parse | Stringify | Extension Required |
|---|---|---|---|
| Date | β | β | dates: true |
| Map | β | β | maps: true |
| Set | β | β | sets: true |
| Undefined | β | β | undefined: true |
| Comments | β | β | comments: true |
Potential additions for continued learning:
- BigInt Support - Handle arbitrarily large integers
- Binary Data - Base64 encoded buffers/typed arrays
- RegExp Serialization - Regular expression support
- Symbol Support - Symbol.for() registry handling
- Performance Benchmarks - Compare against native JSON
- Streaming Parser - Handle large files without loading into memory
- Source Maps - Map parsed values back to original positions
- JSON Schema Validation - Built-in validation during parsing
- Custom Formatting Rules - Configurable code style preferences
- Performance is not optimized (educational focus)
- No streaming support (loads entire input)
- Extensions are format-breaking (not standard JSON)
- Limited to UTF-8 string input
- No async/await support for parsing
- ECMA-404 JSON Specification
- RFC 8259 - The JavaScript Object Notation (JSON) Data Interchange Format
- MDN - JSON.parse()
- MDN - JSON.stringify()
MIT License - Feel free to use this for learning and education.
This is primarily a learning project, but suggestions and improvements are welcome! Feel free to:
- Open issues for bugs or questions
- Submit PRs for enhancements
- Share your own extensions
- Use it for educational purposes
Built as a personal learning journey to deeply understand:
- How parsers work at a fundamental level
- The challenges of data serialization
- Software architecture and modularity
- TypeScript's type system capabilities
Happy Learning! π
If you found this helpful for understanding JSON parsing, please star the repo!