Struct map a `JSON3.Object` to a struct?
DilumAluthge opened this issue · comments
Suppose that I read some JSON in like this:
json_object = JSON3.read(my_json_string) # `json_object` is of type `JSON3.Object`
Can I now convert json_object
to a struct of type T
, where I have done the necessary work to set up struct mapping for T
?
Obviously, I can do this with:
json_object = JSON3.read(my_json_string) # `json_object` is of type `JSON3.Object`
temp_string = JSON3.write(json_object) # `temp_string` is a `String
my_object = JSON3.read(temp_string, T) # now `my_object` is of type `T`
But is there a way to do this that doesn't require me to write it out to an intermediate string? I guess I'm looking for a method struct_map
such that this works:
json_object = JSON3.read(my_json_string)
my_object = JSON3.struct_map(json_object, T) # I want `my_object` to be of type `T`
For what it's worth, in my use case, my type T
will always be of the StructTypes.Mutable()
struct type. Not sure if that's helpful or not.
Can you explain why you can't do json_object = JSON3.read(my_json_string, T)
in your initial read? But in any case, I agree this could be useful; I actually had a use-case for this last night working on wrapping a web api where their documented json model was horribly out of date, so I turned my julia structs into essentially:
mutable struct Obj
id::Any
prop1::Any
prop2::Any
Obj() = new()
end
I ended up with code like:
makeobj(::Type{<:Union{Real, String}}, obj) = obj
makeobj(::Type{Any}, obj) = obj
makeobj(::Type{T}, obj) where {T <: NamedTuple} = NamedTuple(Tuple(k => makeobj(fieldtype(T, k), v) for (k, v) in obj))
function makeobj(::Type{T}, obj) where {T <: AbstractVector}
x = similar(T, 0)
for y in obj
push!(x, makeobj(eltype(T), y))
end
return x
end
function makeobj(::Type{T}, obj) where {T}
x = T()
for (k, v) in obj
if hasfield(T, k)
setfield!(x, k, makeobj(fieldtype(T, k), v))
end
end
return x
end
I think what might make the most sense here is to actually add some code like this to the StructTypes.jl package, that takes any AbstractDict
and a T
and tries to construct an instance of T
from the key-value pairs in the AbstractDict
. Then it would "just work" with JSON3.Object
.
Can you explain why you can't do
json_object = JSON3.read(my_json_string, T)
in your initial read?
Yeah... I don't know ahead of time what kind of object I will get. It's similar to the use case I was talking about on Slack. Except in the Slack use case, I had a field name
that told me which type I would get, so I could use the "abstract type" struct type. In the use case that motivated this issue, I don't even have that. I can find out later which type it is, and thus convert the JSON object to the appropriate type, but it takes some work and time to figure out what the type is.
I think what might make the most sense here is to actually add some code like this to the StructTypes.jl package, that takes any
AbstractDict
and aT
and tries to construct an instance ofT
from the key-value pairs in theAbstractDict
. Then it would "just work" withJSON3.Object
.
This sounds great!
Implemented in JuliaData/StructTypes.jl#16