JSON is typically considered an “anonymous type” kind of specification, so there isn’t really a notion of what “type” an object is. More specifically, there are only a limited set of “types” in JSON:
- Object: specified by
{ ... }
brackets and key-value pairs, w/ keys being strings
- Array: specified by
[ ... ]
brackets, each element being separated by a comma
- String: specified by
" ... "
double quotation marks
- Number: either integer or float values
- Null: the literal
null
value
In this schema, as I mentioned, the closest kind of thing to a custom Julia type is an Object, so the natural representation of your TestType
example is indeed {"a":"Hello"}
. As you’ve noticed, this can pose challenges w/ a type like TestType{A}
in Julia, where the inner field is a parameter and can essentially be one of many types.
In JSON2, this is made a bit simpler by allowing the user to specify the type that they wish to deserialize, like the following example:
julia> struct TTT{A}
x::A
end
julia> a = TTT(1.0)
TTT{Float64}(1.0)
julia> b = TTT("hey")
TTT{String}("hey")
julia> aa = JSON2.write(a)
"{\"x\":1.0}"
julia> bb = JSON2.write(b)
"{\"x\":\"hey\"}"
julia> JSON2.read(aa, TTT{Float64})
TTT{Float64}(1.0)
julia> JSON2.read(bb, TTT{String})
TTT{String}("hey")
In this case, I’m able to read and write our custom struct TTT
with either a Float64 or String inner field because when reading, we’re able to specify the exact type we want to construct, and JSON2 can, using julia’s powerful reflection tools, figure out how to construct one of those types.