|
| 1 | +RFC: JSON-WEB3 Serialization Format |
| 2 | +Status: Draft |
| 3 | +Version: 1 |
| 4 | + |
| 5 | +Abstract |
| 6 | +This document specifies a JSON-compatible serialization format used by json-web3 |
| 7 | +to encode data types that are not supported by standard JSON. The goal is to |
| 8 | +enable interoperability across languages by defining canonical tag objects and |
| 9 | +payload shapes. |
| 10 | + |
| 11 | +Terminology |
| 12 | + |
| 13 | +- "Tag object" means a JSON object with exactly one of the reserved keys defined |
| 14 | + in this document. |
| 15 | +- "Canonical encoding" means the form produced by the reference implementation. |
| 16 | +- "Producer" means a serializer that emits this format. |
| 17 | +- "Consumer" means a parser that restores values from this format. |
| 18 | + |
| 19 | +1. Data Model |
| 20 | + The format is a superset of JSON that preserves: |
| 21 | + |
| 22 | +- BigInt |
| 23 | +- Non-finite numbers (NaN, Infinity, -Infinity) |
| 24 | +- Date |
| 25 | +- RegExp |
| 26 | +- URL |
| 27 | +- Map |
| 28 | +- Set |
| 29 | +- ArrayBuffer |
| 30 | +- TypedArray (including BigInt typed arrays) |
| 31 | +- Optional: Function (UNSAFE mode only) |
| 32 | + |
| 33 | +2. Reserved Tag Keys |
| 34 | + All tag keys are strings that MUST be used exactly as written: |
| 35 | + |
| 36 | +- "__@json.bigint__" |
| 37 | +- "__@json.number__" |
| 38 | +- "__@json.date__" |
| 39 | +- "__@json.regexp__" |
| 40 | +- "__@json.url__" |
| 41 | +- "__@json.map__" |
| 42 | +- "__@json.set__" |
| 43 | +- "__@json.typedarray__" |
| 44 | +- "__@json.arraybuffer__" |
| 45 | +- "__@json.function__" |
| 46 | + |
| 47 | +3. Encoding Rules (Canonical) |
| 48 | + Producers MUST output one of the following tag objects when the source value |
| 49 | + matches the type. All tag objects SHOULD have no other properties. |
| 50 | + |
| 51 | +3.1 BigInt |
| 52 | +{"__@json.bigint__": "<decimal-string>"} |
| 53 | +The decimal string MUST be base-10 with optional leading "-" for negative |
| 54 | +values. No "+" prefix. |
| 55 | + |
| 56 | +3.2 Non-finite Number |
| 57 | +{"__@json.number__": "NaN" | "Infinity" | "-Infinity"} |
| 58 | +Finite numbers MUST be encoded as standard JSON numbers, but a producer MUST |
| 59 | +reject (or error on) any finite number that exceeds the safe integer range |
| 60 | +[-9007199254740991, 9007199254740991]. |
| 61 | + |
| 62 | +3.3 Date |
| 63 | +{"__@json.date__": <milliseconds-since-epoch>} |
| 64 | +The value is a JSON number representing milliseconds since Unix epoch. |
| 65 | + |
| 66 | +3.4 RegExp |
| 67 | +{"__@json.regexp__": {"source": "<pattern>", "flags": "<flags>"}} |
| 68 | +Both "source" and "flags" MUST be strings. |
| 69 | + |
| 70 | +3.5 URL |
| 71 | +{"__@json.url__": "<url-string>"} |
| 72 | + |
| 73 | +3.6 Map |
| 74 | +{"__@json.map__": [[k1, v1], [k2, v2], ...]} |
| 75 | +The payload MUST be a JSON array of 2-element arrays. Order is preserved. |
| 76 | + |
| 77 | +3.7 Set |
| 78 | +{"__@json.set__": [v1, v2, ...]} |
| 79 | +Order is preserved. |
| 80 | + |
| 81 | +3.8 TypedArray (and Node.js Buffer JSON shape) |
| 82 | +{"__@json.typedarray__": {"type": "<CtorName>", "bytes": "0x..."}} |
| 83 | + |
| 84 | +The "type" field MUST be one of: |
| 85 | +Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, |
| 86 | +Int32Array, Uint32Array, Float16Array, Float32Array, Float64Array, |
| 87 | +BigInt64Array, BigUint64Array |
| 88 | + |
| 89 | +"bytes" MUST be a hexadecimal string with a "0x" prefix. Canonical encoding |
| 90 | +uses lowercase hex. The byte sequence is the raw bytes of the view (respecting |
| 91 | +byteOffset and byteLength), not the full underlying buffer. |
| 92 | + |
| 93 | +Node.js Buffer JSON shape is also accepted and normalized: |
| 94 | +{"type":"Buffer","data":[0..255]} -> encoded as type "Uint8Array". |
| 95 | + |
| 96 | +3.9 ArrayBuffer |
| 97 | +{"__@json.arraybuffer__": {"bytes": "0x..."}} |
| 98 | +Same hex format as TypedArray bytes. |
| 99 | + |
| 100 | +3.10 Function (UNSAFE only) |
| 101 | +{"__@json.function__": "<source>"} |
| 102 | +The source is the function's string representation. Consumers MAY refuse to |
| 103 | +revive functions unless explicitly running in UNSAFE mode. |
| 104 | + |
| 105 | +4. Decoding Rules |
| 106 | + Consumers MUST: |
| 107 | + |
| 108 | +- Detect any tag object and reconstruct the corresponding type. |
| 109 | +- Validate the payload structure and types, otherwise raise an error. |
| 110 | +- For __@json.number__, reject any string other than NaN, Infinity, -Infinity. |
| 111 | +- For __@json.typedarray__, if the "type" is unknown, decode as Uint8Array |
| 112 | + using the provided bytes. |
| 113 | + |
| 114 | +5. Collision and Compatibility |
| 115 | + Because tag objects are plain JSON objects, a producer SHOULD avoid emitting |
| 116 | + regular objects that use any reserved tag key with a single-property object |
| 117 | + shape, to prevent accidental decoding. |
| 118 | + |
| 119 | +6. Security Considerations |
| 120 | + |
| 121 | +- The __@json.function__ tag can execute arbitrary code if revived. Only enable |
| 122 | + UNSAFE mode for trusted inputs. |
| 123 | +- Non-finite numbers and BigInt are represented as strings; treat them as |
| 124 | + untrusted input and validate if used in critical logic. |
| 125 | + |
| 126 | +7. IANA Considerations |
| 127 | + None. |
| 128 | + |
| 129 | +8. Reference Implementation |
| 130 | + json-web3 (TypeScript) is the reference implementation of this specification. |
0 commit comments