Natan Rubinstein Natan Rubinstein - 1 month ago 17
JSON Question

Kotlin Jackson generation objects from JSON

Please help! I'm trying to generate object from JSON with jackson kotlin module. Here is json source:

{
"name": "row",
"type": "layout",
"subviews": [{
"type": "horizontal",
"subviews": [{
"type": "image",
"icon": "ic_no_photo",
"styles": {
"view": {
"gravity": "center"
}
}
}, {
"type": "vertical",
"subviews": [{
"type": "text",
"fields": {
"text": "Some text 1"
}
}, {
"type": "text",
"fields": {
"text": "Some text 2"
}
}]
}, {
"type": "vertical",
"subviews": [{
"type": "text",
"fields": {
"text": "Some text 3"
}
}, {
"type": "text",
"fields": {
"text": "Some text 4"
}
}]
}, {
"type": "vertical",
"subviews": [{
"type": "image",
"icon": "ic_no_photo"
}, {
"type": "text",
"fields": {
"text": "Some text 5"
}
}]
}]
}]
}


I'm trying to generate instance of Skeleton class.

data class Skeleton (val type : String,
val name: String,
val icon: String,
val fields: List<Field>,
val styles: Map<String, Map<String, Any>>,
val subviews : List<Skeleton>)

data class Field (val type: String, val value: Any)


As you can see, Skeleton object can have other Skeleton objects inside (and these objects can have other Skeleton objects inside too), also Skeleton can have List of Field objects

val mapper = jacksonObjectMapper()
val skeleton: Skeleton = mapper.readValue(File(file))


This code ends with exception:

com.fasterxml.jackson.databind.JsonMappingException: Instantiation of [simple type, class com.uibuilder.controllers.parser.Skeleton] value failed (java.lang.IllegalArgumentException): Parameter specified as non-null is null: method com.uibuilder.controllers.parser.Skeleton.<init>, parameter name
at [Source: docs\layout.txt; line: 14, column: 3] (through reference chain: com.uibuilder.controllers.parser.Skeleton["subviews"]->java.util.ArrayList[0]->com.uibuilder.controllers.parser.Skeleton["subviews"]->java.util.ArrayList[0])

Answer

There are several issues I found about your mapping that prevent Jackson from reading the value from JSON:

  • Skeleton class has not-null constructor parameters (e.g. val type: String, not String?), and Jackson passes null to them if the value for those parameters is missing in JSON. This is what causes the exception you mentioned:

    Parameter specified as non-null is null: method com.uibuilder.controllers.parser.Skeleton.<init>, parameter name

    To avoid it, you should mark the parameters that might might have values missing as nullable (all of the parameters in your case):

    data class Skeleton(val type: String?,
                        val name: String?,
                        val icon: String?,
                        val fields: List<Field>?,
                        val styles: Map<String, Map<String, Any>>?,
                        val subviews : List<Skeleton>?)
    
  • fields in Skeleton has type List<Field>, but in JSON it's represented by a single object, not by an array. The fix would be to change the fields parameter type to Field?:

    data class Skeleton(...
                        val fields: Field?,
                        ...)
    
  • Also, Field class in your code doesn't match the objects in JSON:

    "fields": {
        "text": "Some text 1"
    }
    

    You should change Field class as well, so that it has text property:

    data class Field(val text: String)
    

After I made the changes I listed, Jackson could successfully read the JSON in question.


See also: "Null Safety" in Kotlin reference.