luckysing_noobster
luckysing_noobster

Reputation: 2013

GSON polymorphic deserialization cannot deserialize `type` property using RuntimeTypeAdapterFactory

I am trying to use gson type adapter to deserialize my JSON. I am able to get the object back deserialized but the type property always returns null.

JSON:

{
"type": "rectangle",
"x": "3",
"y": "3",
"description": "this is my battle ground",
"children": [
{
  "type": "square",
  "x": "3",
  "y": "4",
  "description": "this is my arena",
  "children": [
    {
      "type": "circle",
      "x": "0",
      "y": "0",
      "radius": "3",
      "description": "this is my inner circle"
    },
    {
      "type": "circle",
      "x": "0",
      "y": "0",
      "radius": "4",
      "description": "this is my inner circle"
    },
    {
      "type": "circle",
      "x": "0",
      "y": "0",
      "radius": "5",
      "description": "this is my inner circle"
    }
   ]
  }
 ]
}

Models:

abstract class Shape {
    abstract val type: String
    abstract val x: String
    abstract val y: String
    abstract val description: String
}

abstract class ShapeChildren : Shape() {
    abstract val children: List<Shape>
}


enum class ShapeType(val type: String) {
    SQUARE("square"),
    RECTANGLE("rectangle"),
    CIRCLE("circle")
}

data class Square(override val type: String, override val x: String, override val y: String, override val description: String, override val children: List<Shape>) : ShapeChildren()
data class Rectangle(override val type: String, override val x: String, override val y: String, override val description: String, override val children: List<Shape>) : ShapeChildren()
data class Circle(override val type: String, override val x: String, override val y: String, override val description: String, val radius: String) : Shape()

CONFIG GSON:

 val jsonResponse = loadFile("mock-response/shape.json")
        val adapterFactory = RuntimeTypeAdapterFactory.of(Shape::class.java)
            .registerSubtype(ShapeChildren::class.java)
            .registerSubtype(Square::class.java, "square")
            .registerSubtype(Circle::class.java, "circle")
            .registerSubtype(Rectangle::class.java, "rectangle")
        val gson = GsonBuilder()
            .registerTypeAdapterFactory(adapterFactory)
            .create()
        val shape = gson.fromJson(jsonResponse, Shape::class.java)
        print(shape)

OUTPUT:

Rectangle(type=null, x=3, y=3, description=this is my battle ground, children=[Square(type=null, x=3, y=4, description=this is my arena, children=[Circle(type=null, x=0, y=0, description=this is my inner circle, radius=3), Circle(type=null, x=0, y=0, description=this is my inner circle, radius=4), Circle(type=null, x=0, y=0, description=this is my inner circle, radius=5)])])

The type property is always null for child classes. I even tried to set it by giving it a default value in the constructor. Why does the type property not get populated correctly? what am I missing?

Upvotes: 1

Views: 822

Answers (1)

ruediste
ruediste

Reputation: 2969

Simply use RuntimeTypeAdapterFactory.of(Shape::class.java, "type", true) instead of RuntimeTypeAdapterFactory.of(Shape::class.java). The last parameter is maintainType. You had it set to false, in which case the type property is removed from the json prior to deserialization.

Upvotes: 2

Related Questions