OSON Specification
Version 1.0
OSON, Oracle binary JSON, is a self-contained binary tree encoding for JSON documents. An OSON value stores a root header, a field-name dictionary, and a tree-node segment so that readers can navigate directly to object members, array elements, and scalar values without reparsing JSON text.
This document describes the public wire format at the level needed to identify, validate, traverse, and interoperate with OSON payloads. It follows the compact style of the BSON specification while preserving OSON terminology.
This public subset documents ordinary instance OSON images and the decoder rules needed to read partially updated images for versions 0x01, 0x02, 0x03, and 0x04. The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, MAY, and OPTIONAL are to be interpreted as described by RFC 2119 and RFC 8174.
OSON is a binary representation for the SQL/JSON native JSON datatype defined by ISO/IEC 9075-2:2023. In addition to JSON object, array, string, number, boolean, and null values, OSON can carry SQL scalar payloads such as DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE, BINARY_FLOAT, BINARY_DOUBLE, RAW or BINARY, INTERVAL YEAR TO MONTH, and INTERVAL DAY TO SECOND. The OSON framing described here is sufficient to navigate, copy, and round-trip documents; implementations that need bit-exact interpretation of Oracle-native scalar payload bytes should reuse the corresponding Oracle datatype codecs.
Basic Types
| Type | Meaning |
|---|---|
byte | Unsigned 8-bit value. |
uint16 | Unsigned 16-bit integer, big-endian. |
uint32 | Unsigned 32-bit integer, big-endian. |
uint64 | Unsigned 64-bit integer, big-endian. |
int8, int16, int32, int64 | Signed two's-complement integer of the given width, big-endian for multibyte widths. |
bytes(n) | Exactly n bytes. |
cstring | UTF-8 bytes terminated by 0x00; the terminator is not part of the value. |
utf8(n) | n bytes containing UTF-8 text. |
offset32 | uint32 byte offset from the beginning of the OSON payload unless a version or flag explicitly marks relative-offset mode. |
All multibyte integer fields in OSON are encoded in big-endian order for platform-independent interchange.
Top-Level Format
oson-document ::= magic version body
magic ::= 0xff 0x4a 0x5a // 0xff 'J' 'Z'
version ::= 0x01 | 0x02 | 0x03 | 0x04
body ::= header-fields field-name-dictionary tree-node-segment
| Version | Meaning |
|---|---|
0x01 | Original instance format with one-byte field-name length entries. |
0x02 | Version 1 plus an extended tree segment used by partial update and growth handling. |
0x03 | Version 1 plus two-byte field-name length entries. |
0x04 | Version 3 plus the extended tree segment. |
Logical Layout
oson-instance ::= root-header field-name-dictionary tree-node-segment
field-name-dictionary ::= sorted-field-name-hash-array field-name-offset-array field-name-bytes
tree-node-segment ::= root-node node-record* shared-node-segment? overflow-or-update-segment?
The field-name dictionary assigns a field id (FID) to each distinct object field name. Object nodes store FIDs rather than repeated field names.
Design Rationale
| Goal | Format support |
|---|---|
| Jump navigation | Container nodes store child offsets, allowing a decoder to jump directly to a requested object field or array element after resolving the field id or element index. |
| Dictionary compression | Distinct field names are stored once in the field-name dictionary and are referenced by small FID values in object nodes. |
| Decoder-compatible partial update | Versions 0x02 and 0x04 keep the ordinary node model and add forwarding records so a decoder can resolve updated nodes from an appended extended tree segment. |
| Compact storage | Shared simple nodes, shared scalar nodes, relative offsets, and FID-array sharing reduce space without changing the logical JSON value exposed by a decoder. |
Detailed Segment Layout
2.1 Root Header Structure
The root header starts every ordinary self-contained OSON instance. The first six bytes are always present:
root-header-prefix ::= 0xff 0x4a 0x5a version flag1 flag2
| Byte | Field | Meaning |
|---|---|---|
| 0 | 0xff | JZNOCT_MAGIC. |
| 1 | 0x4a | ASCII J, JZNOCT_MAGICJ. |
| 2 | 0x5a | ASCII Z, JZNOCT_MAGICZ. |
| 3 | version | 0x01, 0x02, 0x03, or 0x04. |
| 4 | flag1 | First persistent root-header flag byte. |
| 5 | flag2 | Second persistent root-header flag byte. |
Version 0x01 is the base instance format. Version 0x02 means the instance may contain partial-update extension structures. Version 0x03 means the instance contains a secondary dictionary for field names whose byte length is greater than 255 and no greater than 65535. Version 0x04 combines the version 3 dictionary extension with the version 2 partial-update extension structures.
flag1 bit values:
| Mask | Name | Meaning |
|---|---|---|
0x01 | JZNOCT_HID_USEUB1 | Primary dictionary hash-id entries use the most significant one byte of the four-byte field-name hash. This bit is expected when primary dictionary entries exist. |
0x02 | JZNOCT_HID_USEUB2 (reserved) | Must be zero. The historical source name indicates a two-byte primary hash-id experiment, but compliant persistent OSON does not set this bit. Decoders MUST reject an instance with this bit set. |
0x04 | JZNOCT_TOT_DISFNM_UB2 | The count of distinct primary field names is stored as uint16; otherwise the count is one byte unless flag2 0x08 selects uint32. Object FID entries for the primary dictionary are also uint16 when this bit is set. |
0x08 | JZNOCT_FLDNM_SZ_UB4 | The primary field-name heap size is stored as uint32; otherwise it is stored as uint16. |
0x10 | JZNOCT_TREE_SZ_UB4 | The tree-node segment size is stored as uint32; otherwise it is stored as uint16. |
0x20 | JZNOCT_TINY_NODE_STAT | Tiny-node statistics are present. A tiny node is an object or array node whose storage is smaller than five bytes, or an object node whose FID array is referenced by another object. Scalar nodes are not counted as tiny nodes. |
0x40 | JZNOCT_HAS_SHR_NODES_SEG_CLUSTER | A clustered shared-node segment is present inside the tree-node segment. In this form the root header's tot-tiny-node field stores the shared-node segment offset; the actual tiny-node count, when present, is stored in the shared-node segment. |
0x80 | JZNOCT_FID_NO_SORT | Object FID arrays in this instance are not globally guaranteed to be sorted by FID. Individual object opcodes can also carry the unsorted-object bit. |
flag2 bit values:
| Mask | Name | Meaning |
|---|---|---|
0x01 | JZNOCT2_REL_OFFSET | Container child offsets are stored relative to the containing object or array node address. A decoder reads the stored uint16 or uint32, then adds it to the parent node offset with the same-width unsigned arithmetic. |
0x02 | JZNOCT2_INLINE_LEAF | Scalar leaf bytes are stored inline in the scalar node. Current encoders set this bit. |
0x04 | JZNOCT2_SLEN_IN_PCODE | Common scalar lengths are encoded in the scalar opcode. Current encoders set this bit together with 0x02. |
0x08 | JZNOCT2_TOT_DISFNM_UB4 | The count of distinct primary field names is stored as uint32. Object FID entries for the primary dictionary are uint32. This overrides flag1 0x04. |
0x10 | JZNOCT2_J_SCALAR | The top-level JSON value is a scalar. The tree-node segment contains a single scalar node. |
0x20 | JZNOCT2_SHR_SIMP_NODES | Sharable simple nodes such as true, false, null, empty string, empty object, empty array, zero, and one may be de-duplicated. |
0x40 | JZNOCT2_SHR_NODES | Sharable scalar nodes may be de-duplicated in addition to simple nodes. |
0x80 | reserved | Must be zero. Decoders MUST reject an instance with this bit set. |
The header fields after flag2 are width-selected by these flags:
root-header ::= root-header-prefix
primary-field-name-count
primary-field-name-heap-size
v3v4-extra-header?
tree-node-segment-size
tot-tiny-node
| Field | Width | Meaning |
|---|---|---|
primary-field-name-count | 1 byte, uint16, or uint32 | Number of distinct field names whose UTF-8 byte length is at most 255. Width is one byte by default, uint16 when flag1 & 0x04 is set, and uint32 when flag2 & 0x08 is set. |
primary-field-name-heap-size | uint16 or uint32 | Size in bytes of the primary field-name heap. Width is selected by flag1 & 0x08. |
v3v4-extra-header | 10 bytes | Present only for version 0x03 or 0x04. |
tree-node-segment-size | uint16 or uint32 | Size in bytes of the tree-node segment. Width is selected by flag1 & 0x10. |
tot-tiny-node | uint16 | Tiny-node count when flag1 & 0x20 is set and no clustered shared-node segment redirects the field. If flag1 & 0x40 is set, this field stores the tree-node-segment offset of the shared-node segment. |
Version 0x03 and 0x04 add the following 10 bytes before tree-node-segment-size:
| Field | Width | Meaning |
|---|---|---|
pflag3 | 1 byte | Additional persistent flag byte for the secondary dictionary. 0x01 (JZNOCT3_FLDNM2_SZ_UB2) means secondary field-name offsets are uint16; otherwise they are uint32. Bits 0x02 through 0x80 are reserved and must be zero. |
pflag4 | 1 byte | Reserved. Must be zero. |
secondary-field-name-count | uint32 | Number of distinct field names whose UTF-8 byte length is greater than 255 and no greater than 65535. |
secondary-field-name-heap-size | uint32 | Size in bytes of the secondary field-name heap. |
2.2 Field Name Dictionary
The field-name dictionary is split into a primary dictionary and, for version 0x03 or 0x04, an optional secondary dictionary. Each dictionary has three arrays in this order:
field-name-dictionary ::= primary-hash-id-array
primary-field-name-offset-array
primary-field-name-heap
secondary-hash-id-array?
secondary-field-name-offset-array?
secondary-field-name-heap?
Primary dictionary entries represent field names whose UTF-8 byte length is at most 255. The primary hash-id array contains one hash-id entry per distinct primary field name. Each hash-id entry is the most significant byte of the fixed four-byte field-name hash. The primary field-name offset array contains one offset per entry; each offset is relative to the start of the primary field-name heap and is uint16 unless flag1 & 0x08 selects uint32. Each primary heap record is:
primary-field-name-record ::= uint8-byte-length utf8(byte-length)
The FID for a primary field name is its zero-based array index in the sorted primary hash-id array. Object nodes store FIDs using one byte by default, uint16 when flag1 & 0x04 is set, and uint32 when flag2 & 0x08 is set.
Secondary dictionary entries represent field names whose UTF-8 byte length is greater than 255 and no greater than 65535. They exist only in version 0x03 or 0x04. Each secondary hash-id entry is the most significant two bytes of the same fixed four-byte field-name hash. Secondary field-name offsets are uint16 when pflag3 & 0x01 is set and uint32 otherwise. Each secondary heap record is:
secondary-field-name-record ::= uint16-byte-length utf8(byte-length)
Secondary FIDs follow all primary FIDs: secondary-fid = primary-field-name-count + secondary-index. A decoder can determine which dictionary to use by comparing the FID with primary-field-name-count.
Hashing and sorting are part of the persistent format. The field-name hash is FNV-1a over the UTF-8 bytes, starting with 0x811c9dc5, multiplying by 16777619 after each XOR, and then treating the result as big-endian for the stored most-significant-byte comparison. Primary entries sort by the most significant hash byte, then one-byte field-name length, then UTF-8 byte comparison. Secondary entries sort by the most significant two hash bytes, then two-byte field-name length, then UTF-8 byte comparison. Hash collisions are resolved by comparing length and then the actual UTF-8 field-name bytes.
2.3 Shared Node Segment
When flag1 & 0x40 (JZNOCT_HAS_SHR_NODES_SEG_CLUSTER) is set, shared nodes are clustered in a shared-node segment embedded in the tree-node segment. The root header's tot-tiny-node field gives the tree-node-segment offset of this segment. The segment is normally placed immediately after the root node, but decoders MUST use the header offset rather than infer the location from the root-node size.
shared-node-segment ::= shared-node-segment-opcode
tiny-node-count?
shared-simple-node-region
shared-scalar-region-length?
shared-scalar-node-region?
The one-byte shared-node-segment-opcode is a bit field:
| Mask | Name | Meaning |
|---|---|---|
0x80 | JZN0_SHARE_SEG_HAS_VARLEN | A shared-scalar-node region is present, and a region length follows the simple-node region. |
0x40 | JZN0_SHARE_SEG_VARLEN_UB4 | The shared-scalar-node region length is uint32; otherwise it is uint16. This bit is meaningful only when 0x80 is set. |
0x3f | simple-region length | Length in bytes of the shared-simple-node region. The maximum directly encoded simple-region length is 63 bytes. |
If flag1 & 0x20 (JZNOCT_TINY_NODE_STAT) is set, tiny-node-count is a uint16 stored immediately after the opcode. If that root flag is clear, no tiny-node count is stored and the count is zero. The shared-simple-node region contains complete OSON node records for simple shared values. The shared-scalar-node region, when present, contains complete scalar node records for de-duplicated scalar values. Child offsets that resolve inside this segment are interpreted exactly like offsets to ordinary tree nodes; the segment changes ownership and update rules, not the node opcode grammar.
2.4 Tree-Node Segment
The tree-node segment is a byte serialization of object, array, and scalar nodes. Every node has a tree-node-segment offset relative to the beginning of this segment. Container child-offset arrays store these offsets as uint16 or uint32; when root flag2 & 0x01 is set, the stored value is relative to the containing node and must be translated to an absolute tree-node-segment offset before dereferencing.
| Bits | Masked value | Node class |
|---|---|---|
00xxxxxx | 0x00 | Scalar opcode family. |
01xxxxxx | 0x40 | Scalar opcode family or partial-update marker. |
10xxxxxx | 0x80 | Object node. |
11xxxxxx | 0xc0 | Array node. |
Container opcode bits:
| Mask | Name | Applies to | Meaning |
|---|---|---|---|
0xc0 | JZNOCT_TYP_BITMASK | object/array | 0x80 (JZNOCT_OBJECT_TYP) means object; 0xc0 (JZNOCT_ARRAY_TYP) means array. |
0x20 | JZNOCT_OFFSET_SIZE_BIT | object/array | Child offsets are uint32 when set and uint16 when clear. |
0x18 | JZNOCT_CHILDREN_SIZE_BIT | object/array | Selects the child-count width: 0x00 one byte, 0x08 uint16, 0x10 uint32. For objects only, 0x18 means the FID array is referenced from a primary object instead of stored inline. |
0x04 | JZNOCT_OBJ_FID_UNSORTED_BIT | object | This object's FID array is not sorted by FID. |
0x02 | JZNOCT_OBJ_FID_REFERRED | object | This object owns a FID array referenced by other object nodes with the same object definition. |
0x01 | JZNOCT_UPD_OBJ_OVFLW | object | This referred object was updated through overflow-address indirection. This bit is valid only with 0x02. |
object-node ::= object-opcode child-count fid-array child-offset-array
object-fid-reference-node ::= object-opcode primary-object-offset child-offset-array
array-node ::= array-opcode child-count child-offset-array
Normal objects store a FID array and a child-offset array with matching cardinality. A FID-array reference object is selected when (opcode & 0x18) == 0x18; its referenced primary object MUST be an object with JZNOCT_OBJ_FID_REFERRED set. Arrays use the same child-count and child-offset width bits as objects, but 0x18 is reserved for object FID-array references and MUST NOT be used as an array child-count width.
Scalar opcodes use the low six bits as scalar type and, for compact families, inline length. The scalar opcode reference below gives the payload for each scalar opcode. The partial-update marker opcodes are 0x75 (JZNOCT_UPD_OVFLW), 0x76 (JZNOCT_UPD_UB2_FWA), 0x77 (JZNOCT_UPD_UB4_FWA), and 0x78 (JZNOCT_UPD_XSZ_RES). A decoder MUST resolve these markers to the replacement node before exposing the logical value.
Conceptual Grammar
OSON_FORMAT := FIELD_NAME_DICTIONARY ROOT_NODE_OFFSET NODE_SEGMENT
FIELD_NAME_DICTIONARY := SORTED_FIELD_NAME_HASH_CODE_ARRAY FIELD_NAME_OFFSET_ARRAY FIELD_NAME_HEAP
FID(field-name) := index of field-name in SORTED_FIELD_NAME_HASH_CODE_ARRAY
JUMP_TO(ROOT_NODE_OFFSET) := OBJECT_NODE | ARRAY_NODE | SCALAR_NODE | FORWARD_OFFSET
OBJECT_NODE := OBJECT_OPCODE OBJECT_CONTENT
OBJECT_CONTENT := SORTED_FID_ARRAY CHILD_NODE_OFFSET_ARRAY | SORTED_FID_ARRAY_OFFSET CHILD_NODE_OFFSET_ARRAY
ARRAY_NODE := ARRAY_OPCODE CHILD_NODE_OFFSET_ARRAY
SCALAR_NODE := SCALAR_OPCODE SCALAR_LENGTH? SCALAR_BYTES?
JUMP_TO(FORWARD_OFFSET) := NODE_OFFSET
Field-Name Dictionary
| Component | Description |
|---|---|
| Sorted hash array | Hash values for field names, sorted to make lookup efficient. |
| Field-name offset array | Offsets from the dictionary base to each field-name byte string. |
| Field-name heap | UTF-8 field names. Version 1 and 2 entries use one-byte name lengths; version 3 and 4 entries use two-byte name lengths. |
Node Kinds
| Node kind | Content |
|---|---|
| Object | A count, sorted FID array or FID-array reference, and child node offsets. |
| Array | A count and child node offsets in array order. |
| Scalar | A scalar opcode, optional length, and scalar bytes. Small scalars can be encoded entirely in the opcode. |
| Forwarding offset | Offset to a replacement node, used by partial update. |
Opcode Reference
This section lists the persistent node opcodes and opcode patterns used by OSON instance documents. All integer counts, offsets, lengths, and fixed-width scalar values following an opcode are stored in big-endian order unless otherwise stated.
| Opcode bits | Hex range | Node class | Decoder action |
|---|---|---|---|
00xxxxxx | 0x00-0x3f | Scalar or scalar extension | Interpret using the scalar opcode table. |
01xxxxxx | 0x40-0x7f | Scalar, scalar extension, or partial-update marker | Interpret using the scalar and update opcode tables. |
10xxxxxx | 0x80-0xbf | Object node | Interpret low bits as object flags, count size, and offset size. |
11xxxxxx | 0xc0-0xff | Array node | Interpret low bits as array flags, count size, and offset size. |
Object and Array Node Opcodes
Container opcodes are bit fields rather than one fixed byte per shape.
| Bit mask | Name | Applies to | Meaning |
|---|---|---|---|
0xc0 | node type | object/array | 0x80 means object; 0xc0 means array. |
0x20 | offset width | object/array | Off: child offsets are uint16. On: child offsets are uint32. |
0x18 | child-count width | object/array | 0x00: one-byte count; 0x08: uint16; 0x10: uint32; 0x18: object-only FID-array reference. |
0x04 | object FID unsorted | object | Object field-id array is not sorted by FID. |
0x02 | object FID referred | object | This object owns a field-id array referenced by other object nodes with the same object definition. |
0x01 | object update overflow | object | Used with 0x02 during partial update when a referenced object definition is updated through overflow indirection. |
object-normal ::= opcode child-count field-id-array child-offset-array
object-fid-ref ::= opcode primary-object-offset child-offset-array
array ::= opcode child-count child-offset-array
| Opcode | Meaning |
|---|---|
0x80 | Object with one-byte child count and uint16 child offsets. |
0x84 | Object with one-byte child count, uint16 child offsets, and the object FID-unsorted flag set; commonly used for the compact empty-object image 0x84 0x00. |
0x88 | Object with uint16 child count and uint16 child offsets. |
0x90 | Object with uint32 child count and uint16 child offsets. |
0xa0 | Object with one-byte child count and uint32 child offsets. |
0xa8 | Object with uint16 child count and uint32 child offsets. |
0xb0 | Object with uint32 child count and uint32 child offsets. |
0x98 | Object FID-array reference with uint16 primary-object offset. |
0xb8 | Object FID-array reference with uint32 primary-object offset. |
0xc0 | Array with one-byte child count and uint16 child offsets; the compact empty-array image is 0xc0 0x00. |
0xc8 | Array with uint16 child count and uint16 child offsets. |
0xd0 | Array with uint32 child count and uint16 child offsets. |
0xe0 | Array with one-byte child count and uint32 child offsets. |
0xe8 | Array with uint16 child count and uint32 child offsets. |
0xf0 | Array with uint32 child count and uint32 child offsets. |
Scalar Node Opcodes
| Opcode or range | Name | Payload after opcode | Decoded value | Notes |
|---|---|---|---|---|
0x00-0x1f | short UTF-8 string | n UTF-8 bytes, where n = opcode & 0x1f | JSON string | Covers lengths 0 through 31. |
0x20-0x2f | short Oracle NUMBER | n Oracle NUMBER bytes, where n = (opcode & 0x0f) + 1 | JSON number | Covers lengths 1 through 16. |
0x30 | null | none | JSON null | No payload. |
0x31 | true | none | JSON boolean true | No payload. |
0x32 | false | none | JSON boolean false | No payload. |
0x33 | UTF-8 string with one-byte length | uint8 length, then UTF-8 bytes | JSON string | For lengths less than 256. |
0x34 | Oracle NUMBER with one-byte length | uint8 length, then Oracle NUMBER bytes | JSON number | Length must be nonzero. |
0x35 | numeric text | uint8 length, then numeric text bytes | JSON number | Internal/test number text path. |
0x36 | binary double | 8 bytes | SQL/JSON binary double | Oracle/canonical binary double form. |
0x37 | UTF-8 string with uint16 length | uint16 length, then UTF-8 bytes | JSON string | Long string. |
0x38 | UTF-8 string with uint32 length | uint32 length, then UTF-8 bytes | JSON string | Very large string. |
0x39 | timestamp | 11 bytes | SQL timestamp | Oracle timestamp with fractional seconds. |
0x3a | binary with uint16 length | uint16 length, then bytes | Binary/raw | For binary payloads smaller than 64 KiB. |
0x3b | binary with uint32 length | uint32 length, then bytes | Binary/raw | For larger binary payloads. |
0x3c | date | 7 bytes | SQL date | Oracle date binary form. |
0x3d | year-month interval | 5 bytes | SQL interval year to month | Years use bias +2147483648; months use bias +60. |
0x3e | day-second interval | 11 bytes | SQL interval day to second | Oracle interval day-second binary form. |
0x3f | other/reserved extension | implementation-defined | implementation-defined | Reject unless explicitly supported. |
0x40-0x47 | signed 32-bit integer as Oracle NUMBER | n = opcode & 0x07 Oracle NUMBER bytes | Number | Valid lengths 1 through 7. |
0x48-0x4f | reserved | none | none | Reject unless assigned by a future version. |
0x50-0x5f | signed 64-bit integer as Oracle NUMBER | n = opcode & 0x0f Oracle NUMBER bytes | Number | Valid lengths 1 through 12. |
0x60-0x6f | Decimal128 as Oracle NUMBER | n = (opcode & 0x0f) + 1 Oracle NUMBER bytes | Decimal128-preserving number | Covers lengths 1 through 16. |
0x70-0x73 | reserved | none | none | Reject unless assigned by a future version. |
0x74 | Decimal128 Oracle NUMBER with one-byte length | uint8 length, then Oracle NUMBER bytes | Decimal128-preserving number | For payload length greater than 16. |
0x75 | update overflow marker | none in node | forwarding node | Forwarding address is in the overflow-address map. |
0x76 | update uint16 forwarding address | uint16 forwarding-address | forwarding node | Partial-update indirection. |
0x77 | update uint32 forwarding address | uint32 forwarding-address | forwarding node | Partial-update indirection. |
0x78 | update reserved-growth wrapper | one growth-size byte, then another node | wrapped node | Extra room for in-place growth. |
0x79 | native integer | subtype byte, then integer bytes | Integer number | See native-integer subtype table. |
0x7a | reserved | none | none | Reject unless assigned by a future version. |
0x7b | extended binary type | subtype byte, uint32 length, then payload | Extended scalar | See extended-binary subtype table. |
0x7c | timestamp with time zone | 13 bytes | SQL timestamp with time zone | 11 timestamp bytes plus 2 time-zone bytes. |
0x7d | timestamp7 | 7 bytes | SQL timestamp | Zero fractional seconds. |
0x7e | generic ID | uint8 length, then ID bytes | ID value | Used for ObjectId, UUID, ROWID, and similar IDs. |
0x7f | binary float | 4 bytes | SQL/JSON binary float | Oracle/canonical binary float form. |
Native Integer Subtypes
| Subtype | Payload | Meaning |
|---|---|---|
0x01 | 1 byte | Unsigned 8-bit integer. |
0x81 | 1 byte | Signed 8-bit integer. |
0x02 | 2 bytes | Unsigned 16-bit integer, big-endian. |
0x82 | 2 bytes | Signed 16-bit integer, two's-complement big-endian. |
0x04 | 4 bytes | Unsigned 32-bit integer, big-endian. |
0x84 | 4 bytes | Signed 32-bit integer, two's-complement big-endian. |
0x08 | 8 bytes | Unsigned 64-bit integer, big-endian. |
0x88 | 8 bytes | Signed 64-bit integer, two's-complement big-endian. |
Extended Binary Subtypes
| Subtype | Meaning | Payload |
|---|---|---|
0x01 | Vector | Vector embedding binary image. |
0x02 | Embedded OSON | Reserved for OSON embedded in OSON. Portable encoders should avoid it unless both endpoints define the payload contract. |
0x03 | Scalar array | Packed homogeneous scalar-array image. |
Decoder Validation for Opcodes
- Reject scalar opcodes whose length fields exceed the containing tree segment.
- Reject Oracle NUMBER opcode families with zero payload length where the family requires a nonzero length.
- Reject object or array opcodes with child-count width
0x18unless the node is an object FID-array reference. - Reject array opcodes with object-only flags set.
- Reject object FID-array references whose primary-object offset does not resolve to an object with the referred bit set.
- Reject partial-update forwarding chains that point outside the original or extended tree segment, or that cycle.
- Reject reserved opcodes or subtypes unless explicitly supported.
Scalar Types
| Family | Payload |
|---|---|
| Null | No payload. |
| Boolean true/false | No payload. |
| UTF-8 string | Length plus UTF-8 bytes. Compact string opcodes can encode short lengths in the opcode. |
| JSON number | Oracle NUMBER bytes, decimal text/number form, IEEE float/double, Decimal64/Decimal128, or native integer form depending on encoder choice and type preservation flags. |
| Native integer | int8, unsigned byte, int16, uint16, int32, uint32, int64, or uint64 in two's-complement big-endian form. The native-integer extension opcode is 0x79. |
| Binary | Length plus uninterpreted bytes. |
| ID | Fixed-length binary identifiers such as BSON ObjectId, UUID, or Oracle ROWID. UUID is 16 bytes. |
| Date | Oracle date bytes. |
| Timestamp | Oracle timestamp bytes, either 7-byte timestamp-with-zero-fraction form or 11-byte full timestamp form. |
| Timestamp with time zone | Oracle timestamp-with-time-zone bytes. Public opcode family includes 0x7c. |
| Year-month interval | Oracle year-month interval bytes. Public opcode family includes 0x3d. |
| Day-second interval | Oracle day-second interval bytes. Public opcode family includes 0x3e. |
| Binary float | IEEE 754 binary32 or Oracle binary-float bytes. Public opcode family includes 0x7f. |
| Binary double | IEEE 754 binary64 or Oracle binary-double bytes. Public opcode family includes 0x36. |
| Vector | Extended binary type for vector embeddings. The vector payload uses the extended binary type container. |
| Scalar array | Extended binary type for homogeneous SQL scalar arrays. |
Compact Encoding and Shared Nodes
| Mode | Description | Partial-update implication |
|---|---|---|
| No compact sharing | Each logical node has its own physical node. | A scalar can be directly replaced when the new encoded value fits in the old node. |
| Shared simple nodes | Common simple values such as true, false, null, empty string, empty object, and empty array can be shared. | Shared simple nodes cannot be directly overwritten for one occurrence. |
| Shared scalar nodes | Repeated scalar values can be shared. | A shared scalar cannot be directly overwritten for one occurrence. |
| Clustered shared segment | Shared nodes are stored together in a shared-node segment embedded in the tree-node segment. | Readers treat child offsets into the shared segment as normal node references; updaters must preserve sharing semantics. |
Section 3. OSON Segments for Partial Update
OSON supports direct in-place replacement when an updated node fits in the original node storage. Direct replacement does not change the OSON version and does not append any segment. When the updated content does not fit, OSON appends update structures after the original tree-node segment and changes the instance version from 0x01 to 0x02, or from 0x03 to 0x04 when the instance uses the secondary field-name dictionary.
version-1-or-3-layout ::= root-header field-name-dictionary tree-node-segment
version-2-or-4-layout ::= root-header field-name-dictionary tree-node-segment
update-header overflow-address-map-segment extended-tree-segment
The overflow-address-map segment can have size zero when the original document has no tiny nodes. A tiny node is a node whose original storage is too small to hold a forwarding opcode plus a forwarding address. New content produced by partial update is always written into the extended tree segment. Original nodes that no longer hold their current content are rewritten as forwarding markers.
3.1 Update Header
The update header is fixed-size and immediately follows the original tree-node segment. JZNOCT_UPD_HEADER_SZ is 16 bytes.
update-header ::= update-flag1 update-flag2 overflow-entry-count
reserved4 overflow-map-size extended-tree-size
| Offset | Field | Width | Meaning |
|---|---|---|---|
| 0 | update-flag1 | 1 byte | First persistent partial-update flag byte. |
| 1 | update-flag2 | 1 byte | Reserved. Must be zero. Decoders MUST reject a nonzero value. |
| 2 | overflow-entry-count | uint16 | Number of used tiny-node address mappings in the overflow-address-map segment. Maximum is JZNOCT_UPD_OVFLW_MAX_ENTRY, 1024. |
| 4 | reserved4 | 4 bytes | Reserved. Must be all zero. Decoders MUST reject nonzero bytes. |
| 8 | overflow-map-size | uint32 | Allocated size in bytes of the overflow-address-map segment. This size is chosen when the update segments are first appended and remains fixed for the instance. Maximum is JZNOCT_UPD_MAX_OVFLW_ADDR_SEG_SZ, 8192. |
| 12 | extended-tree-size | uint32 | Allocated or used size in bytes of the extended tree segment so far. It grows as update appends add new node images. |
update-flag1 mask | Name | Meaning |
|---|---|---|
0x01 | JZNOCTUPDHDR_OVFLW_SEG_UB2 | Overflow-address-map entries are (uint16 original-node-offset, uint16 forwarding-offset), 4 bytes per entry. |
0x02 through 0x80 | reserved | Must be zero. Decoders MUST reject an instance with any of these bits set. |
update-flag2 is reserved as an entire byte. It MUST be zero.
3.2 Overflow-Address-Map Segment
update-flag1 & 0x01 | Entry layout | Entry size |
|---|---|---|
| Set | uint16 original-node-offset, uint16 forwarding-offset | 4 bytes |
| Clear | uint32 original-node-offset, uint32 forwarding-offset | 8 bytes |
The first value in each entry is the tree-node-segment offset of the original tiny node. The second value is the forwarding offset for the replacement node in the extended tree segment. All values are big-endian. Decoders should load this segment into an address map before resolving JZNOCT_UPD_OVFLW or object-reference overflow markers. Encoders choose the 4-byte entry form when the original tree plus allowed growth fits in the uint16 address space; otherwise they use the 8-byte form.
3.3 Extended Tree Segment
The extended tree segment stores complete OSON node images appended by partial update. A forwarding offset is relative to the beginning of the extended tree segment address space. To resolve it, add the forwarding offset to:
extended-tree-base = original-tree-node-segment-size
+ JZNOCT_UPD_HEADER_SZ
+ overflow-map-size
max-growth = (original-tree-node-segment-size
+ JZNOCT_UPD_HEADER_SZ
+ JZNOCT_UPD_MAX_OVFLW_ADDR_SEG_SZ) * 0.25
If partial update would exceed the overflow map limit or the extended tree growth limit, the writer should replace the full OSON document with a newly encoded compact instance rather than append more update records.
3.4 Partial-Update Forwarding Opcodes and Bits
| Opcode or mask | Name | Meaning |
|---|---|---|
0x75 (01110101) | JZNOCT_UPD_OVFLW | Original node is a tiny node. Its replacement forwarding offset is stored in the overflow-address-map segment under this original node offset. |
0x76 (01110110) | JZNOCT_UPD_UB2_FWA | The next two bytes are a big-endian uint16 forwarding offset into the extended tree segment. The marker occupies 3 bytes total. |
0x77 (01110111) | JZNOCT_UPD_UB4_FWA | The next four bytes are a big-endian uint32 forwarding offset into the extended tree segment. The marker occupies 5 bytes total. |
0x78 (01111000) | JZNOCT_UPD_XSZ_RES | Reserved-growth wrapper. The next byte records reserved growth size; the real node begins after the two-byte wrapper. |
mask 0x83 (10000011) | JZNOCT_UPD_OBJ_REF_BITMASK | Object-reference overflow marker. ((opcode & 0x83) == 0x83) means an object whose FID array is referred by other objects has been updated through overflow indirection. |
For JZNOCT_UPD_OVFLW and the object-reference overflow marker, the original node offset is looked up in the overflow-address-map segment. For JZNOCT_UPD_UB2_FWA and JZNOCT_UPD_UB4_FWA, the forwarding offset is stored inline after the marker opcode. Readers MUST reject cycles and out-of-range forwarding targets.
3.5 Partial-Update Writer Rules
- If replacement content fits in the old node storage, rewrite the node in place and leave the version unchanged.
- If replacement content does not fit but the old node has room for
0x76plus two bytes, writeJZNOCT_UPD_UB2_FWAand auint16forwarding offset. - If replacement content does not fit but the old node has room for
0x77plus four bytes, writeJZNOCT_UPD_UB4_FWAand auint32forwarding offset. - If the old node is too small for an inline forwarding address, write
JZNOCT_UPD_OVFLWor the object-reference overflow bit pattern and add an overflow-address-map entry. - If any size limit is exceeded, perform a full document replacement.
3.6 Partial-Update Reader Algorithm
A decoder reads a version 0x02 or 0x04 image as an ordinary OSON tree until it reaches a forwarding marker. It then resolves the marker to a complete replacement node in the extended tree segment and decodes that replacement node using the same object, array, and scalar rules.
parse-update-extension:
update-start = tree-start + original-tree-node-segment-size
read update-flag1, update-flag2, overflow-entry-count
require update-flag2 == 0
require overflow-entry-count <= 1024
require reserved4 == 0
read overflow-map-size and extended-tree-size
require overflow-map-size <= 8192
map-start = update-start + 16
extended-tree-start = map-start + overflow-map-size
require extended-tree-start + extended-tree-size <= payload-size
read overflow-entry-count pairs into overflow-map
resolve-forwarding-marker(original-offset, opcode):
if opcode == 0x75:
forward = overflow-map[original-offset]
else if opcode == 0x76:
forward = uint16 at original-offset + 1
else if opcode == 0x77:
forward = uint32 at original-offset + 1
else if ((opcode & 0x83) == 0x83):
forward = overflow-map[original-offset]
else if opcode == 0x78:
reject reserved growth wrapper
require forward exists and forward < extended-tree-size
decode node at extended-tree-start + forward
A decoder MUST bounds-check every forwarding address before dereferencing it. A decoder SHOULD reject forwarding cycles or excessive forwarding depth. Valid images resolve forwarding markers to ordinary node content without requiring unbounded chains.
Decoder Algorithm
The following pseudocode summarizes the behavior of a conformant decoder. It is not a required API shape, but the checks and dispatch order are normative.
Parse Header and Locate Root
parse-header:
require bytes[0..2] == 0xff 0x4a 0x5a
version = byte[3]
require version in {0x01, 0x02, 0x03, 0x04}
flag1 = byte[4]
flag2 = byte[5]
reject reserved flag bits
pos = 6
if flag2 & 0x10: // top-level scalar
read tree-node-segment-size
tree-start = pos
return root node at tree-start
read primary-field-name-count using width selected by flag1/flag2
read primary-field-name-heap-size using width selected by flag1
if version in {0x03, 0x04}:
read pflag3, pflag4, secondary-field-name-count, secondary-field-name-heap-size
require reserved secondary flag bits are zero
read tree-node-segment-size
read tot-tiny-node or shared-node-segment offset
read field-name dictionary segment
tree-start = current position
if version in {0x02, 0x04}:
parse update extension after original tree-node segment
Decode Node at Tree Offset
decode-node(tree-offset):
absolute = tree-start + tree-offset
require absolute is inside the active tree segment
opcode = byte[absolute]
if version in {0x02, 0x04} and opcode is a forwarding marker:
return resolve-forwarding-marker(tree-offset, opcode)
if (opcode & 0xc0) == 0xc0:
decode array node
else if (opcode & 0xc0) == 0x80:
decode object node
else:
decode scalar node by opcode table
reject unknown opcodes, reserved opcodes, and out-of-range payload lengths
Object Field Lookup and Offset Resolution
object-get(object-offset, field-name):
fid = dictionary lookup by hash, length, and UTF-8 bytes
if fid is absent:
return absent
read object child count, FID array, and child-offset array
if the object FID array is sorted:
index = binary search FID array for fid
else:
index = linear search FID array for fid
if index is absent:
return absent
raw = child-offset[index]
if root flag2 has REL_OFFSET:
raw = raw + object-offset
return decode-node(raw)
A decoder MUST treat dictionary sizes, tree sizes, scalar payload lengths, and child offsets as untrusted input. It MUST bounds-check each computed address against the segment that owns the address before reading from it.
Validation Rules
- The magic and version are recognized.
- All offsets point inside the OSON payload or the active segment.
- Object FID arrays are sorted and have the same cardinality as child offset arrays.
- FIDs reference existing dictionary entries.
- Field-name lengths do not exceed the version's maximum length field.
- UTF-8 field names and JSON strings are well-formed when declared as UTF-8.
- Scalar payload lengths match the scalar opcode family.
- Forwarding offsets terminate at a concrete node.
- Shared-node references point to valid node records.
- Reserved flags and reserved opcode ranges are either ignored only where specified as ignorable, or rejected.
Conformance
Conformant Decoder
- A conformant decoder MUST verify the magic bytes and MUST accept versions
0x01,0x02,0x03, and0x04. - It MUST reject unrecognized versions, reserved root-header flag bits, reserved secondary-dictionary flag bits, reserved update-header flag bits, and reserved scalar opcodes unless a later public extension defines them.
- It MUST implement dictionary lookup using the stored field-name hash id, field-name length, and UTF-8 byte comparison, and MUST resolve FIDs across both primary and secondary dictionaries.
- It MUST decode all object, array, scalar, native-integer, extended-binary, and partial-update forwarding opcodes defined by this document.
- It MUST support relative-offset mode, FID-array sharing, shared-node images, and version
0x02/0x04forwarding. - It MUST bounds-check all offsets, lengths, child counts, and forwarding targets before dereferencing them.
Conformant Encoder
- A conformant encoder MUST emit the magic bytes and an appropriate version. It SHOULD use version
0x01unless it needs the secondary dictionary or partial-update extension; it SHOULD use version0x03when any field name is longer than 255 bytes. - It MUST set
JZNOCT2_INLINE_LEAFandJZNOCT2_SLEN_IN_PCODE, and MUST setJZNOCT_HID_USEUB1whenever the primary dictionary is non-empty. - It MUST NOT set reserved root-header, secondary-dictionary, or update-header flag bits.
- It MUST build dictionaries using the specified FNV-1a hash, byte order, sort key, and FID numbering rules.
- It MUST emit multibyte integers in big-endian byte order and select width flags consistently with the emitted sizes.
- It MAY omit compact shared-node production, relative-offset production, and FID-array sharing; images without those optimizations remain conformant.
Interoperability Notes
The dictionary hash byte order, FID sort order, and baseline encoder flags are common sources of cross-implementation defects. An implementation SHOULD test images that use long field names, unsorted object FID arrays, relative offsets, shared FID arrays, and forwarded partial-update nodes, because those features exercise the decoder paths most likely to diverge between independent implementations.
Comparison With BSON
| Property | BSON | OSON |
|---|---|---|
| Object field names | Stored inline with each element. | Stored once in a per-instance field-name dictionary and referenced by FID. |
| Traversal | Mostly sequential within an object. | Tree offsets allow direct jump navigation. |
| Top-level value | Historically document-oriented. | Any JSON value can be top-level. |
| Partial update | Not a primary layout goal. | Supported through node sizing, forwarding offsets, and update segments. |
| SQL scalar types | Limited extension set. | Includes SQL/JSON scalar extensions such as dates, timestamps, intervals, binary values, and vectors. |
Extension Rules
Future OSON versions can add scalar opcode families and flags.
- Writers SHOULD use the lowest version that can represent the value.
- Readers MUST reject unknown required versions.
- Readers SHOULD preserve unknown binary scalar payloads when operating in a lossless copy mode.
- Public extensions SHOULD define the opcode family, payload length rules, byte order, and comparison semantics.
Section 4. JSON Binary Tree Encoding Example
Given this JSON document:
{
"person": {
"id": "123",
"name": "john",
"birthdate": "1970-01-02",
"friends": [
{ "person": { "id": "456", "name": "Mary", "birthdate": "1968-04-03" } },
{ "person": { "id": "789", "name": "Henry", "birthdate": "1972-03-03" } }
],
"location": { "city": "Oakland", "zip": "94403" }
}
}
A version 1 OSON image for this value can be summarized as follows:
| Component | Example value | Meaning |
|---|---|---|
| Magic | 0xff 0x4a 0x5a | OSON document signature. |
| Version | 0x01 | Base instance format. |
flag1 | 0x21 | Primary hash-id byte is present and tiny-node statistics are recorded. |
flag2 | 0x06 | Inline scalar values and scalar lengths encoded in opcodes. |
| Total distinct field names | 8 | The dictionary has eight FIDs. |
| Field-name heap size | 51 bytes | Concatenated field-name heap size. |
| Tree-node segment size | 143 bytes | Serialized tree nodes for the value. |
| Tiny-node statistic | 1 | One tiny node is recorded for update planning. |
The field-name dictionary stores each distinct field name once. The example uses one-byte hash ids and uint16 field-name offsets:
| FID | Hash-id byte | Field-name heap offset | Field name |
|---|---|---|---|
| 1 | 42 | 15 | birthdate |
| 2 | 66 | 42 | city |
| 3 | 128 | 0 | person |
| 4 | 166 | 33 | location |
| 5 | 180 | 47 | zip |
| 6 | 218 | 25 | friends |
| 7 | 224 | 7 | id |
| 8 | 230 | 10 | name |
The root node is an object with one field, person, whose FID is 3 and whose child node offset is 5. The person object then contains five fields: id, name, birthdate, friends, and location. The scalar string values use short-string opcodes where the low bits carry the string length.
nodeOff=0 OBJECT opcode=0x84 numFields=1 { fid=3,nodeOff=5 }
nodeOff=5 OBJECT opcode=0x84 numFields=5 { fid=7,nodeOff=22; fid=8,nodeOff=26; fid=1,nodeOff=31; fid=6,nodeOff=42; fid=4,nodeOff=121 }
nodeOff=22 SCALAR opcode=0x03 string len=3 "123"
nodeOff=26 SCALAR opcode=0x04 string len=4 "john"
nodeOff=31 SCALAR opcode=0x0a string len=10 "1970-01-02"
nodeOff=42 ARRAY opcode=0xc0 numFields=2 [ nodeOff=48; nodeOff=84 ]
nodeOff=48 OBJECT opcode=0x86 field-id array referred, numFields=1 { fid=3,nodeOff=53 }
nodeOff=53 OBJECT opcode=0x84 numFields=3 { fid=7,nodeOff=64; fid=8,nodeOff=68; fid=1,nodeOff=73 }
nodeOff=64 SCALAR opcode=0x03 string len=3 "456"
nodeOff=68 SCALAR opcode=0x04 string len=4 "Mary"
nodeOff=73 SCALAR opcode=0x0a string len=10 "1968-04-03"
nodeOff=84 OBJECT opcode=0x9c object field-id reference, numFields=1 { fid=3,nodeOff=89 }
nodeOff=89 OBJECT opcode=0x84 numFields=3 { fid=7,nodeOff=100; fid=8,nodeOff=104; fid=1,nodeOff=110 }
nodeOff=100 SCALAR opcode=0x03 string len=3 "789"
nodeOff=104 SCALAR opcode=0x05 string len=5 "Henry"
nodeOff=110 SCALAR opcode=0x0a string len=10 "1972-03-03"
nodeOff=121 OBJECT opcode=0x84 numFields=2 { fid=2,nodeOff=129; fid=5,nodeOff=137 }
nodeOff=129 SCALAR opcode=0x07 string len=7 "Oakland"
nodeOff=137 SCALAR opcode=0x05 string len=5 "94403"
This example illustrates the main OSON navigation pattern: resolve a field name to its FID through the dictionary, binary-search or scan the object's FID array according to its sortedness flags, then jump directly to the matching child node offset.
Example
{"a": true, "b": [1, 2], "c": "cat"}
magic/version
field-name dictionary: ["a", "b", "c"]
root object:
FID("a") -> scalar true
FID("b") -> array node
[0] -> scalar integer 1
[1] -> scalar integer 2
FID("c") -> scalar string "cat"
Machine-Readable Registry
The following JSON fragments summarize the flag and opcode assignments for code generators and decoder test tools. The prose and tables above are authoritative if there is any discrepancy.
Flags
{
"byte_order": "big-endian",
"magic": [255, 74, 90],
"versions": {
"1": "base",
"2": "base+partial-update-extension",
"3": "long-field-names",
"4": "long-field-names+partial-update-extension"
},
"flag1": {
"JZNOCT_HID_USEUB1": "0x01",
"reserved": "0x02",
"JZNOCT_TOT_DISFNM_UB2": "0x04",
"JZNOCT_FLDNM_SZ_UB4": "0x08",
"JZNOCT_TREE_SZ_UB4": "0x10",
"JZNOCT_TINY_NODE_STAT": "0x20",
"JZNOCT_HAS_SHR_NODES_SEG_CLUSTER": "0x40",
"JZNOCT_FID_NO_SORT": "0x80"
},
"flag2": {
"JZNOCT2_REL_OFFSET": "0x01",
"JZNOCT2_INLINE_LEAF": "0x02",
"JZNOCT2_SLEN_IN_PCODE": "0x04",
"JZNOCT2_TOT_DISFNM_UB4": "0x08",
"JZNOCT2_J_SCALAR": "0x10",
"JZNOCT2_SHR_SIMP_NODES": "0x20",
"JZNOCT2_SHR_NODES": "0x40",
"reserved": "0x80"
},
"update_header": {
"size": 16,
"update_flag1": {"JZNOCTUPDHDR_OVFLW_SEG_UB2": "0x01"},
"update_flag2": "reserved_zero",
"overflow_entry_count_max": 1024,
"overflow_map_size_max": 8192
},
"node_header": {
"object_range": "0x80-0xbf",
"array_range": "0xc0-0xff",
"offset_size_bit": "0x20",
"children_size_bits": "0x18",
"fid_no_sort": "0x04",
"fid_referred": "0x02",
"update_object_overflow": "0x01"
}
}
Scalar Opcodes
[
{"range": "0x00-0x1f", "type": "string", "length": "opcode & 0x1f"},
{"range": "0x20-0x2f", "type": "number", "length": "(opcode & 0x0f)+1"},
{"op": "0x30", "type": "null"},
{"op": "0x31", "type": "true"},
{"op": "0x32", "type": "false"},
{"op": "0x33", "type": "string", "length_prefix": "uint8"},
{"op": "0x34", "type": "number", "length_prefix": "uint8"},
{"op": "0x35", "type": "numeric_string", "length_prefix": "uint8"},
{"op": "0x36", "type": "binary_double", "fixed_length": 8},
{"op": "0x37", "type": "string", "length_prefix": "uint16"},
{"op": "0x38", "type": "string", "length_prefix": "uint32"},
{"op": "0x39", "type": "timestamp", "fixed_length": 11},
{"op": "0x3a", "type": "binary", "length_prefix": "uint16"},
{"op": "0x3b", "type": "binary", "length_prefix": "uint32"},
{"op": "0x3c", "type": "date", "fixed_length": 7},
{"op": "0x3d", "type": "interval_year_month", "fixed_length": 5},
{"op": "0x3e", "type": "interval_day_second", "fixed_length": 11},
{"range": "0x40-0x47", "type": "number_int32", "length": "opcode & 0x07"},
{"range": "0x50-0x5f", "type": "number_int64", "length": "opcode & 0x0f"},
{"range": "0x60-0x6f", "type": "decimal", "length": "(opcode & 0x0f)+1"},
{"op": "0x74", "type": "decimal", "length_prefix": "uint8"},
{"range": "0x75-0x77", "type": "partial_update_forwarding_marker"},
{"op": "0x78", "type": "reserved_partial_update_growth_wrapper"},
{"op": "0x79", "type": "native_integer_extension"},
{"op": "0x7b", "type": "extended_binary", "subtypes": {"0x01": "vector", "0x02": "embedded_oson", "0x03": "scalar_array"}},
{"op": "0x7c", "type": "timestamp_with_time_zone", "fixed_length": 13},
{"op": "0x7d", "type": "timestamp7", "fixed_length": 7},
{"op": "0x7e", "type": "id", "length_prefix": "uint8", "max_length": 127},
{"op": "0x7f", "type": "binary_float", "fixed_length": 4},
{"range": "0x80-0xbf", "type": "object_node"},
{"range": "0xc0-0xff", "type": "array_node"}
]
Implementation References
Independent implementations can use this specification directly. Public Oracle client drivers are useful as reference implementations and test oracles for scalar payload handling, dictionary lookup, and node traversal behavior.
| Implementation | Notes | URL |
|---|---|---|
| python-oracledb | Open source Python driver with thin-mode JSON support. | https://oracle.github.io/python-oracledb/ |
| python-oracledb source | Source repository for the Python driver. | https://github.com/oracle/python-oracledb |
| Oracle JDBC driver JSON support | Java client implementation guidance and APIs for Oracle JSON values. | https://docs.oracle.com/en/database/oracle/oracle-database/23/jjdbc/working-with-json-data.html |
Background and Context Information
The following public references provide background on OSON, Oracle native JSON storage, and binary JSON format tradeoffs.