Skip to content

Json decoder factory - #10670

Draft
hareshkh wants to merge 2 commits into
apache:mainfrom
hareshkh:json-decoder-factory
Draft

Json decoder factory#10670
hareshkh wants to merge 2 commits into
apache:mainfrom
hareshkh:json-decoder-factory

Conversation

@hareshkh

@hareshkh hareshkh commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

What changes are included in this PR?

  • TapeElement #[non_exhaustive], documenting numbers as Number (JSON text) or native i32/i64/f32/f64 (serde path), 64-bit spanning two elements.
  • ArrayDecoder public; pos holds one tape index per output row.
  • New DecoderFactory, consulted before the reader's dispatch; Ok(None) accepts the default.
  • DecoderContext::make_decoder public, so a factory can delegate to the decoder the reader would otherwise use - without it, overriding a nested type means reimplementing its children (@scovich's point on Allow extensions to arrow-json decoder and include an extension for variant #9021).
  • ReaderBuilder::with_decoder_factory.

Are these changes tested?

Yes. Unit tests and a doctest decoding Binary from a JSON int array - the inverse of the existing EncoderFactory doctest, so the two round-trip, no new dependency.

Are there any user-facing changes?

New: arrow_json::{Tape, TapeElement, ArrayDecoder, DecoderFactory}, DecoderContext::{make_decoder, decoder_factory}, ReaderBuilder::with_decoder_factory.

Preparatory work for allowing custom decoders in the arrow-json reader.
A custom `ArrayDecoder` necessarily reads the tape, so `Tape` and
`TapeElement` have to be public for such a hook to be usable.

Rather than making `mod tape` public, this re-exports just the two types
that a decoder needs. `TapeDecoder` stays private, so downstream code can
read a `Tape` it is handed but cannot construct one, keeping the tape's
production an implementation detail.

`TapeElement` is marked `#[non_exhaustive]`. Its representation is an
implementation detail — 64-bit values are split across two consecutive
elements, and the type's own docs note that offsets may become a custom
`u56` type — so downstream matches need a wildcard arm for that to remain
a non-breaking change.

Also documents two aspects of the tape that become public contract:
string data is copied into the tape with escapes resolved, and numbers
appear either as `Number` (parsing JSON text) or as the native
`I32`/`I64`/`F32`/`F64` variants (serializing Rust values), so decoders
must handle both.
@github-actions github-actions Bot added arrow Changes to the arrow crate arrow-json labels Aug 12, 2026
@hareshkh
hareshkh force-pushed the json-decoder-factory branch from 91479ca to 63e65ba Compare August 12, 2026 23:37
The JSON writer has supported overriding how a type is encoded since apache#7015
via `EncoderFactory`. The reader has had no equivalent, so anything the
built-in decoders don't do — a different binary encoding, an extension type
— requires forking the crate.

This adds the reader-side counterpart:

* `ArrayDecoder` is now public, so callers can implement a decoder.
* `DecoderFactory` is consulted for every data type before the reader's own
  dispatch, and returns `Ok(None)` to accept the default.
* `DecoderContext::make_decoder` is now public, so a factory can delegate to
  the decoder the reader would otherwise have used. Without this, overriding
  anything nested would mean reimplementing all of its children.
* `ReaderBuilder::with_decoder_factory` registers a factory.

Prior art: apache#9021 and apache#9272, both of which went stale.
@hareshkh
hareshkh force-pushed the json-decoder-factory branch from 63e65ba to d38a0dd Compare August 12, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arrow Changes to the arrow crate arrow-json

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant