A hand-written JSON, YAML, and XML parser built from scratch in TypeScript — no regex hacks, no external parsing libraries. Every format is parsed with a proper recursive-descent / state-machine approach.
- Full RFC 8259 compliance
- Objects, arrays, strings, numbers, booleans, null
- Unicode escape sequences (
\uXXXX) - Nested structures of arbitrary depth
- Detailed error messages with position tracking
- YAML 1.2 subset support
- Block mappings and sequences (indentation-based)
- Flow style (
{}and[]) - Quoted strings (single & double)
- Block scalars (
|literal and>folded) - Comments (
#) - Booleans, integers, floats, null (
~,null, empty)
- Elements (open/close, self-closing)
- Attributes (double & single quoted)
- Text content & CDATA sections
- XML declaration (
<?xml ...?>) - Comments (
<!-- ... -->) - Processing instructions (
<?...?>) - Entity decoding (
&,<,A,A, etc.) - Namespace prefixes (preserved in tag/attribute names)
- Simplified object output mode
├── src/
│ ├── parsers/
│ │ ├── json-parser.ts # Recursive-descent JSON parser
│ │ ├── yaml-parser.ts # Indentation-aware YAML parser
│ │ ├── xml-parser.ts # State-machine XML parser
│ │ └── index.ts # Re-exports
│ ├── __tests__/
│ │ ├── json-parser.test.ts
│ │ ├── yaml-parser.test.ts
│ │ └── xml-parser.test.ts
│ └── index.ts # CLI entry point
├── package.json
├── tsconfig.json
├── jest.config.js
└── README.md
git clone https://github.com/<your-username>/universal-parser.git
cd universal-parser
npm installimport { parseJson, parseYaml, parseXml, parseXmlToObject } from './parsers';
// JSON
const data = parseJson('{"name": "Alice", "age": 30}');
// → { name: 'Alice', age: 30 }
// YAML
const config = parseYaml(`
database:
host: localhost
port: 5432
credentials:
user: admin
password: secret
`);
// → { database: { host: 'localhost', port: 5432, credentials: { user: 'admin', password: 'secret' } } }
// XML (tree structure)
const feed = parseXml('<feed><item>Hello</item></feed>');
// → XmlNode { type: 'element', name: 'feed', attributes: {}, children: [...] }
// XML (simplified object)
const obj = parseXmlToObject('<person name="Alice"><age>30</age></person>');
// → { _name: 'person', _attrs: { name: 'Alice' }, age: { _name: 'age', _text: '30' } }# Build first
npm run build
# Auto-detect format by extension
node dist/index.js data.json
node dist/index.js config.yaml
node dist/index.js feed.xml
# Explicit format
node dist/index.js --json data.json
node dist/index.js --yaml config.yaml
node dist/index.js --xml feed.xml
node dist/index.js --xml-obj feed.xml # Simplified object output
# Read from stdin
echo '{"key": "value"}' | node dist/index.js --stdin jsonnpm test74 tests covering all three parsers — primitives, containers, nested structures, edge cases, and error handling.
- No regex for parsing. Tokenization is character-by-character. This gives precise error reporting and avoids the fragility of regex-based parsers.
- No external parsing libraries. Each parser is implemented from scratch, making this project a good reference for learning how parsers work.
- TypeScript throughout. Full type safety with strict mode enabled.
- Clear error messages. Every parser throws a typed error with position/line information.
- YAML: Does not support anchors/aliases (
&/*), complex keys, or type tags (!!str). Covers the most commonly used subset. - XML: Does not validate against DTD or XSD. Does not resolve namespace URIs (prefixes are preserved as-is).
- JSON: Faithfully implements the spec; no JSON5 or other extensions.
MIT