I’ve been playing around with @quinnj’s JSON2.jl
package a little more in the last few days. For me the two standout features are:
-
'.'
notation (getproperty
) for field access (in JSON2 this is achived by usingNamedTuples
) - automagically filling in a
struct
from a JSON text usingJSON2.read(json_string, StructType)
I’ve been inspired to add similar capabilities to the latest version of LazyJSON by:
- putting a
getproperty
wrapper aroundLazyJSON.Object
so that'o.field'
is syntaxtic sugar foro["field"]
(the field lookup is still done by the usual lazygetindex
method). - adding a
Base.convert(::Type{T}, ::LazyJSON.Object}
method that constructs newstructs
from JSON text.
e.g. '.'
notation for fields:
julia> arrow_json =
"""{
"label": "Hello",
"segments": [
{"a": {"x": 1, "y": 1}, "b": {"x": 2, "y": 2}},
{"a": {"x": 2, "y": 2}, "b": {"x": 3, "y": 3}}
],
"dashed": false
}"""
julia> lazy_arrow = LazyJSON.value(arrow_json)
julia> lazy_arrow.segments[1].a.x
1
e.g. convert
to struct
:
julia> struct Point
x::Int
y::Int
end
julia> struct Line
a::Point
b::Point
end
julia> struct Arrow
label::String
segments::Vector{Line}
dashed::Bool
end
julia> convert(Arrow, lazy_arrow)
Arrow("Hello", Line[Line(Point(1, 1), Point(2, 2)), Line(Point(2, 2), Point(3, 3))], false)
Implementation notes:
'.'
notation is implemented by:
Base.getproperty(d::PropertyDict, n::Symbol) = getindex(d, String(n))
convert
is implemented by a @generated function
that generates somthing like:
function convert{::Type{Arrow}, o::JSON.Object)
i = o.i
i, label = get_field(o.s, "label", i)
i, segments = get_field(o.s, "segments", i)
i, dashed = get_field(o.s, "dashed", i)
Arrow(label, segments, dashed)
end
The local variable i
keeps track of the current string index to make finding the fields faster (if they are in the expected order). The call to get_field(o.s, "segments", i)
returns a LazyJSON.Array
object. When this is passed to the Arrow
constructer, Julia automatically calls convert(Vector{Line}, ...)
to convert it to the type of the struct
field. In this way the conversion process works recursively for arbitrarily nested structs.
But, if you care about minimising copying and memory allocation, and you don’t need to access all the fields in a JSON object, it might be better to do away with structs and use the '.'
notation feature to access the JSON values in-place. In my experience with web services APIs it is quite common to have a whole mess of JSON returned by an API request, when I’m only interested in a few particular fields.