2

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])
Natan Rubinstein
  • 1,873
  • 3
  • 11
  • 13

1 Answers1

5

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.

hotkey
  • 140,743
  • 39
  • 371
  • 326
  • Is there a way to use default values for constructor parameters during deserialization if json field values are null? also keeping the fields in data class non-nullable? – denvercoder9 Aug 18 '19 at 06:26