Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Errors deserialising structs without fields #270

Open
freemin7 opened this issue Sep 17, 2023 · 4 comments
Open

Errors deserialising structs without fields #270

freemin7 opened this issue Sep 17, 2023 · 4 comments

Comments

@freemin7
Copy link
Contributor

freemin7 commented Sep 17, 2023

I have a wrapper type which might contains empty object which might be omitted.

using StructTypes 

abstract type Foo end

struct TypedWrapper{T}
    wrap::T
    type::Symbol
end

struct Intainter <: Foo a::Int end
struct NoBrainer <: Foo end

StructTypes.StructType(::Type{TypedWrapper}) = StructTypes.AbstractType()
StructTypes.subtypekey(::Type{TypedWrapper}) = :type
StructTypes.subtypes(::Type{TypedWrapper}) = (brain = TypedWrapper{NoBrainer},  int = TypedWrapper{Intainter})

@inline StructTypes.isempty(::Type{TypedWrapper}, ::NoBrainer) = true
StructTypes.omitempties(::Type{TypedWrapper}) = (:wrap)

StructTypes.StructType(::Type{TypedWrapper{T}}) where T <: Foo = StructTypes.Struct()

using JSON3

TestBuffer = PipeBuffer()

for m in [ TypedWrapper(Intainter(8),:int) TypedWrapper(NoBrainer(),:brain) ]
    println(TestBuffer, JSON3.write(m))
    println(JSON3.write(m))
end

# Problems begin here

for i in 1:2
  println(JSON3.read(TestBuffer, TypedWrapper))  ## Just prints 1 line when it should print 2 lines
end
JSON3.write(TypedWrapper(NoBrainer(),:brain)) ## Shouldn't serialize the struct as NoBrainer()

JSON3.read("{\"type\":\"brain\"}"
, TypedWrapper) ## shouldn't error
JSON3.read("{\"wrap\":{},\"type\":\"brain\"}"
, TypedWrapper) ## shouldn't error
@quinnj
Copy link
Owner

quinnj commented Sep 18, 2023

Ah, part of the issue here is that StructTypes now has a new struct type SingletonType that is applied automatically to NoBrainer, thus resulting in the serialization like "{\"wrap\":\"NoBrainer()\",\"type\":\"brain\"}". If you define StructTypes.StructType(::Type{NoBrainer}) = StructTypes.Struct(), then your example works for me. The other error can be solved by defining an appropriate constructor that takes nothing, like TypedWrapper{NoBrainer}(::Nothing, type::Symbol) = TypedWrapper{NoBrainer}(NoBrainer(), type) (we attempted to document this but I know it's a bit buried. The key idea is that if you have structs that can be constructed when fields are missing in the JSON, then you should handle that in a custom constructor).

@freemin7
Copy link
Contributor Author

What's the motivation behind the singleton type?

@quinnj
Copy link
Owner

quinnj commented Sep 19, 2023

It helps in other (non-JSON) serialization use-cases and for JSON, provides a more descriptive serialization form (instead of just the default empty object output {}).

@freemin7
Copy link
Contributor Author

I am not sure which one is more intuitive and should be the default behavior. Do you think better documentation about this should go into JSON3 or StructTypes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants