Skip to content

Minimal working example for serializing/deserializing abstract types without exposing :type field #315

Description

@vincent-picaud

Hi,

After some experimentation, I finally found a minimal working solution to serialize & deserialize structs containing abstract type instances.
I did not want to be intrusive by adding an extra :type field to the structures being serialized. I had a bit of trouble figuring out how to make it work, so I’m sharing my code here in case it helps someone else. Also, I’d appreciate if someone could double-check it for correctness or offer improvements.

Here's the working example:

  # Stuff to serialize/deserialize
  #
  abstract type AbstractShape end

  struct Circle <: AbstractShape
      radius::Float64                                # note: no extra :type field
  end

  struct Square <: AbstractShape
      side::Float64                                  # note: no extra :type field
  end

  struct ShapeHolder
      name::String
      shape::AbstractShape
  end

  # ================================================================

  using JSON3, StructTypes

  # ================================================================

  StructTypes.StructType(::Type{AbstractShape}) = StructTypes.AbstractType()
  StructTypes.subtypekey(::Type{AbstractShape}) = :type

  StructTypes.subtypes(::Type{AbstractShape}) = (circle=Circle,square=Square)

  # ================================================================

  StructTypes.StructType(t::Type{Circle}) = StructTypes.CustomStruct() # Mandatory otherwise lower() is not called
  StructTypes.lower(h::Circle) = Dict(
      "type" =>  "circle", 
      "radius" => h.radius
  )

  StructTypes.StructType(t::Type{Square}) = StructTypes.CustomStruct() # Mandatory otherwise lower() is not called
  StructTypes.lower(h::Square) = Dict(
      "type" =>  "square", 
      "side" => h.side
  )

  StructTypes.construct(::Type{Circle}, x::Dict) = Circle(x["radius"])
  StructTypes.construct(::Type{Square}, x::Dict) = Square(x["side"])

  # ================================================================

  function StructTypes.construct(t::Type{AbstractShape}, x::Dict)
      # field containing instance type information
      type_field = String(StructTypes.subtypekey(t))
      # get type information as string
      type_field_value = x[type_field]
      # get type
      type = StructTypes.subtypes(type_field_value)
      # create instance
      StructTypes.construct(type,x)
  end 

Now the example in action

  h = ShapeHolder("my shape", Circle(3.0))
  
  # serialize
  json_str = JSON3.write(h)         # is "{\"name\":\"my shape\",\"shape\":{\"radius\":3.0,\"type\":\"circle\"}}"

  # deserialize
 parsed = JSON3.read(json_str, ShapeHolder)     # is ShapeHolder("my shape", Circle(3.0))  

Thank you for your package !
Best,
Vincent

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions