# OxygenBasic JSON parser update

Date: 2026-04-15

## Scope

This update turns the existing demo-only JSON splitter into a reusable system module.

The parser is now shipped as `inc/JsonUtil.inc` and is also pulled into `inc/ParseUtil.inc`. Because `SysUtil.inc` already depends on `ParseUtil.inc`, the JSON API is also available transitively to existing `SysUtil` consumers.

## Changed files

- `inc/JsonUtil.inc` *(new)*
- `inc/ParseUtil.inc` *(updated: imports `JsonUtil`)*
- `demos/DataProcessing/JSONparsing.o2bas` *(updated: rewritten as a system-module demo)*
- `MASTER_JSON_PARSER_UPDATE.md` *(new)*
- `MASTER_JSON_PARSER_PATCH.diff` *(new)*

## Public API

### Global helpers

- `JsonEscape(string s) as string`
  - Escapes a plain string for JSON output.

- `JsonQuote(string s) as string`
  - Returns a JSON string literal including quotes.

- `JsonKindName(int k) as string`
  - Converts the internal node kind constant to text.

- `JsonFormat(string s, int pretty=1) as string`
  - Parse + re-emit JSON in pretty or compact form.

- `JsonMinify(string s) as string`
  - Compact formatting shortcut.

- `JsonValid(string s) as int`
  - Returns `1` for valid JSON, otherwise `0`.

### Node kind constants

- `JSON_OBJECT`
- `JSON_ARRAY`
- `JSON_STRING`
- `JSON_NUMBER`
- `JSON_TRUE`
- `JSON_FALSE`
- `JSON_NULL`
- `JSON_INVALID`

### `JsonDoc` class

#### Parse and status

- `Parse(string s) as int`
  - Returns the root node id on success, `0` on failure.

- `Valid() as int`
- `ErrorText() as string`
- `ErrorPos() as int`

#### Document metadata

- `RootId() as int`
- `NodeCount() as int`

#### Node inspection

- `NodeKind(int n) as int`
- `NodeTypeName(int n) as string`
- `NodeKey(int n) as string`
- `NodeValue(int n) as string`
- `ParentId(int n) as int`
- `NodeLevel(int n) as int`
- `NodePos(int n) as int`
- `NodeStop(int n) as int`
- `FirstChild(int n) as int`
- `NextSibling(int n) as int`
- `ItemCount(int n) as int`

#### Navigation

- `FindChild(int parent, string key) as int`
  - Linear search across direct children.

- `Item(int parent, index) as int`
  - Zero-based child lookup. Intended for arrays, but works on any child list.

- `Query(string path, int start=0) as int`
  - Lightweight path helper.
  - Supported forms:
    - `$.employees[0].firstName`
    - `employees[1]`
    - `[0]` for a root array

#### Output

- `Stringify(int pretty=0) as string`
  - Rebuilds the current DOM as JSON.

## Behavior notes

- Objects and arrays preserve input order.
- Strings are decoded for the standard short escapes: `\\`, `\"`, `\/`, `\b`, `\f`, `\n`, `\r`, `\t`.
- `\uXXXX` escapes are decoded directly when the code point fits into a byte value (`<=255`).
- `\uXXXX` values above `255` are preserved as literal escape text inside the stored string value. This keeps round-tripping stable without forcing a wide-string dependency into the base module.
- Array indexing in `Item()` and `Query(...[n]...)` is zero-based.

## Minimal usage

```basic
uses JsonUtil

string s = `{ "items": [ { "id": 10 }, { "id": 20 } ] }`
new JsonDoc jd

if jd.Parse(s)
  int n = jd.Query("$.items[1].id")
  print jd.NodeValue(n)
endif

del jd
```

## Integration intent

This update is source-level integration, not a separate add-on utility. The parser lives in the shipped `inc` tree and is imported by `ParseUtil.inc`, which also makes it available transitively to `SysUtil.inc` users. It can be treated like the rest of the built-in utility layer.
