| | | 1 | | # Tables.jl interface for `JSONWorksheet`. |
| | | 2 | | # |
| | | 3 | | # Reference: https://tables.juliadata.org/stable/implementing-the-interface/ |
| | | 4 | | # |
| | | 5 | | # `JSONWorksheet` is naturally row-oriented (`jws.data` is a Vector of |
| | | 6 | | # `PointerDict`s), so row access is the primary path. We additionally synthesise |
| | | 7 | | # column access for column-oriented sinks (DataFrames, CSV.jl, ...). |
| | | 8 | | # |
| | | 9 | | # Column names are exposed as `Symbol`s built from the JSON Pointer path |
| | | 10 | | # (`/a/b` -> `Symbol("/a/b")`). Schema element types come from the `Pointer{T}` |
| | | 11 | | # parameter when available. |
| | | 12 | | |
| | | 13 | | # --- Table-level declarations ------------------------------------------------- |
| | | 14 | | |
| | 1 | 15 | | Tables.istable(::Type{<:JSONWorksheet}) = true |
| | 1 | 16 | | Tables.rowaccess(::Type{<:JSONWorksheet}) = true |
| | 1 | 17 | | Tables.columnaccess(::Type{<:JSONWorksheet}) = true |
| | | 18 | | |
| | 1 | 19 | | Tables.rows(jws::JSONWorksheet) = [JSONWorksheetRow(jws, i) for i in 1:length(jws.data)] |
| | 1 | 20 | | Tables.columns(jws::JSONWorksheet) = JSONWorksheetColumns(jws) |
| | | 21 | | |
| | 4 | 22 | | Tables.columnnames(jws::JSONWorksheet) = |
| | 16 | 23 | | ntuple(i -> _column_symbol(jws.pointer[i]), length(jws.pointer)) |
| | | 24 | | |
| | 1 | 25 | | function Tables.schema(jws::JSONWorksheet) |
| | 6 | 26 | | types = ntuple(i -> _pointer_eltype(jws.pointer[i]), length(jws.pointer)) |
| | 1 | 27 | | Tables.Schema(Tables.columnnames(jws), types) |
| | | 28 | | end |
| | | 29 | | |
| | 6 | 30 | | Tables.getcolumn(jws::JSONWorksheet, i::Int) = jws[:, i] |
| | 1 | 31 | | Tables.getcolumn(jws::JSONWorksheet, p::Pointer) = jws[:, p] |
| | 2 | 32 | | function Tables.getcolumn(jws::JSONWorksheet, nm::Symbol) |
| | 2 | 33 | | for p in jws.pointer |
| | 7 | 34 | | _column_symbol(p) === nm && return jws[:, p] |
| | 5 | 35 | | end |
| | 0 | 36 | | return jws[:, JSONPointer.Pointer(string(nm))] |
| | | 37 | | end |
| | | 38 | | |
| | 4 | 39 | | function Tables.matrix(jws::JSONWorksheet; transpose::Bool=false) |
| | 2 | 40 | | m = jws[:, :] |
| | 2 | 41 | | transpose && return permutedims(m) |
| | 1 | 42 | | m isa AbstractVector && return reshape(m, :, 1) |
| | 1 | 43 | | return m |
| | | 44 | | end |
| | | 45 | | |
| | 24 | 46 | | _column_symbol(p::Pointer) = Symbol("/" * join(p.tokens, "/")) |
| | 4 | 47 | | _pointer_eltype(::Pointer{T}) where {T} = T |
| | | 48 | | |
| | | 49 | | # --- AbstractRow wrapper ------------------------------------------------------ |
| | | 50 | | # |
| | | 51 | | # Subtyping `Tables.AbstractRow` provides automatic `getindex`, `propertynames`, |
| | | 52 | | # `getproperty`, iteration, and `show`. Note: `getproperty` on an `AbstractRow` |
| | | 53 | | # is overridden to dispatch through `getcolumn`, so internal field access must |
| | | 54 | | # go through `getfield`. |
| | | 55 | | |
| | | 56 | | struct JSONWorksheetRow <: Tables.AbstractRow |
| | 3 | 57 | | parent::JSONWorksheet |
| | | 58 | | index::Int |
| | | 59 | | end |
| | | 60 | | |
| | 2 | 61 | | function Tables.getcolumn(row::JSONWorksheetRow, i::Int) |
| | 2 | 62 | | par = getfield(row, :parent) |
| | 2 | 63 | | par[getfield(row, :index), i] |
| | | 64 | | end |
| | 1 | 65 | | function Tables.getcolumn(row::JSONWorksheetRow, p::Pointer) |
| | 1 | 66 | | par = getfield(row, :parent) |
| | 1 | 67 | | par[getfield(row, :index), p] |
| | | 68 | | end |
| | 1 | 69 | | function Tables.getcolumn(row::JSONWorksheetRow, nm::Symbol) |
| | 1 | 70 | | par = getfield(row, :parent) |
| | 1 | 71 | | idx = getfield(row, :index) |
| | 1 | 72 | | for p in par.pointer |
| | 1 | 73 | | _column_symbol(p) === nm && return par[idx, p] |
| | 0 | 74 | | end |
| | 0 | 75 | | return par[idx, JSONPointer.Pointer(string(nm))] |
| | | 76 | | end |
| | 1 | 77 | | Tables.columnnames(row::JSONWorksheetRow) = |
| | | 78 | | Tables.columnnames(getfield(row, :parent)) |
| | | 79 | | |
| | | 80 | | # --- AbstractColumns wrapper -------------------------------------------------- |
| | | 81 | | |
| | | 82 | | struct JSONWorksheetColumns <: Tables.AbstractColumns |
| | 1 | 83 | | parent::JSONWorksheet |
| | | 84 | | end |
| | | 85 | | |
| | 2 | 86 | | Tables.getcolumn(c::JSONWorksheetColumns, i::Int) = |
| | | 87 | | Tables.getcolumn(getfield(c, :parent), i) |
| | 0 | 88 | | Tables.getcolumn(c::JSONWorksheetColumns, p::Pointer) = |
| | | 89 | | Tables.getcolumn(getfield(c, :parent), p) |
| | 1 | 90 | | Tables.getcolumn(c::JSONWorksheetColumns, nm::Symbol) = |
| | | 91 | | Tables.getcolumn(getfield(c, :parent), nm) |
| | 1 | 92 | | Tables.columnnames(c::JSONWorksheetColumns) = |
| | | 93 | | Tables.columnnames(getfield(c, :parent)) |
| | 0 | 94 | | Tables.schema(c::JSONWorksheetColumns) = |
| | | 95 | | Tables.schema(getfield(c, :parent)) |