Skip to content

[Spanner] Add FromValue for serde_json::Value - enable deserialization of STRUCT and ARRAY columns #5860

@buu-nguyen

Description

@buu-nguyen

Problem

row.try_get::<String, _>("struct_column") fails because STRUCT values arrive as
Kind::List (positional ListValue in protobuf), but FromValue for String
expects Kind::String. There is no FromValue impl that handles STRUCT or ARRAY
values natively.

This blocks all use cases that read STRUCT/ARRAY columns from Spanner query
results:

  • Spanner change stream consumption (READ_* TVF returns deeply nested
    ARRAY<STRUCT<ARRAY<STRUCT<...>>>>)
  • INFORMATION_SCHEMA queries returning complex column types
  • Any application query on tables with STRUCT or ARRAY columns

Current workaround (painful, error-prone)

Users must manually walk the Value tree and reconstruct named fields
from type metadata:

// Read raw value (works because FromValue for Value just clones)
let raw = row.try_get::<Value, _>("struct_column")?;

// Manually iterate the ARRAY<STRUCT> value
let list = raw.try_as_list().ok_or(/* ... */)?;
for elem in list.iter() {
    // STRUCT values arrive as positional ListValue on the wire.
    // Field names exist only in Type metadata, not in the data payload.
    // Users must reconstruct named access themselves:
    if let Some(s) = elem.try_as_struct() {
        let field_val = s.get("field_name").ok_or(/* ... */)?;
    }
}

For positional STRUCTs (Spanner's wire format for change streams), users must
hardcode positional indices or manually walk model::Type.struct_type.fields to
reconstruct field names — a non-trivial recursive algorithm.

What already works

The entire public API surface needed for the implementation already exists:

API Status
Value::kind() ✅ Public
Value::try_as_list(), Value::try_as_struct() ✅ Public
Value::as_string(), Value::as_bool(), Value::try_as_f64() ✅ Public
Type::code() ✅ Public
Type::array_element_type() ✅ Public
Into<model::Type> for Type (accesses struct_type) ✅ Public
model::StructType and model::struct_type::Field ✅ Public (constructable)
serde_json dependency ✅ Already in workspace & spanner crate

No new public API methods are required. The implementation can use the
existing Into<model::Type> conversion to read struct_type.fields for
named-field reconstruction.

Related

  • array_element_type() is already public (types.rs:134) — struct_type() is the
    last missing piece for complete type introspection, but not strictly required
    since Into<model::Type> is available.
  • serde_json is already a dependency (workspace Cargo.toml:406, spanner
    Cargo.toml:54).

Metadata

Metadata

Assignees

Labels

api: spannerIssues related to the Spanner API.priority: p3Desirable enhancement or fix. May not be included in next release.type: feature request‘Nice-to-have’ improvement, new feature or different behavior or design.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions