jackson-module-kotlin
jackson-module-kotlin copied to clipboard
Invalid JSON input: Cannot deserialize value of type
I have a data class as following:
@Json
data class ControlTag(val tagSource: TagSource, val tag: TagType)
TagSource class:
@Json
enum class TagSource {
EXISTING, USER
}
TagType is an interface:
interface TagType {
fun getLabel(): String
fun getValue(): String
}
I have several enum classes using this TagType interface:
enum class TagInformation : TagType {
TAG_INFO_YES {
override fun getLabel(): String {
return "Tag Info - Yes"
}
override fun getValue(): String {
return "t"
}
},
TAG_INFO_NO {
override fun getLabel(): String {
return "Tag Info - No"
}
override fun getValue(): String {
return "f"
}
};
@JsonValue
fun value(): String {
return getLabel()
}
}
Everything is ok at backend side, but when I call from ui it gives jackson error:
[
....
{tagSource: "EXISTING", tag: "Tag Info - Yes"}
{tagSource: "USER", tag: "X Enum Info - No"}
{tagSource: "EXISTING", tag: "Y Enum Info - Yes"}
....
]
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
...control_tag.TagType(no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: (PushbackInputStream); line: 1, column: 378] (through reference chain:
I have fixed above issue with adding below
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes(
JsonSubTypes.Type(value = TagInformation ::class, name = "TagInformation ")
)
interface TagType {
But now I have mapping issue:
{ tagSource: 'EXISTING', tag: { type: 'TagInformation', value: 'Tag - Yes' } }
Error is:
2020-04-24 18:10:50.147 WARN 26184 --- [nio-8093-exec-5] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Invalid JSON input: Cannot deserialize value of type
.control_tag.TagInformationfrom String "value": not one of the values accepted for Enum class: [Tag - Yes, Tag - No];
Couple of things: from data class, this seems to be for language other than Java (Kotlin?), so may need to move.
But aside from that, what is needed is little bit code to show exactly how content is deserialized -- that is, show how ObjectMapper.readValue() (or ObjectReader.readValue()) is called.
That detail is often important (esp. regarding target type used; sometimes configuration of mapper itself) in reproducing the issue.
I will move this to Kotlin repo, and add tag to indicate bit more info needed (unit test ideal, but simple reproduction code works as well).
I have a spring boot app with kotlin, I am using vuejs for front end side. This error occurs when I can rest endpoint from ui.
Also following small code piece gives same error
fun main() {
val json = "{\"value\":\"Tag Info - Yes\", \"type\":\"TagInfo\"}"
val result = ObjectMapper().readValue(json, TagType::class.java)
println(result)
}
@mgnfcnt thank you for the reproduction: that should be helpful in figuring this out. It may even be possible to distill this into Java-only test eventually.
One last (I hope :) ) question: which version of Jackson (esp jackson-module-kotlin) is this with? 2.10.3?
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.10.2</version>
<scope>compile</scope>
</dependency>
Actually I didnt add it. It comes with default spring boot web dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Ok. Close enough: I don't think there are many fixes between 2.10.2 and 2.10.3.
@cowtowncoder I have upgraded spring version to
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
My new versions are:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.10.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.10.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<version>2.10.3</version>
<scope>compile</scope>
</dependency>
But still same error
Update: Following code doesn't complain,
companion object {
@JvmStatic
@JsonCreator
fun fromTag(tag: String): TagInfo? {
return getEnum(tag)
}
fun getEnum(requestedValue: String?): TagInfo? {
for (value in values()) {
if (value.getLabel() == requestedValue)
return value
}
return null
}
}
But It passes the param as what I provided at key instead value
val json = "{\"tag\":\"Tag - Yes\", \"type\":\"TagInfo\"}"
val result = ObjectMapper().readValue(json, TagType::class.java)
So because of there is no value as "tag" it returns null,
Following form is working as desired(for now) but it is not proper key/value for json.(Provided value as key)
val json = "{\"Tag - Yes\":\"Tag - Yes\", \"type\":\"TagInfo\"}"