Reputation: 7331
I've already deserialized some nested field in the past in Java, following instructions from https://www.baeldung.com/jackson-nested-values (section 5) :
@JsonProperty("brand")
private void unpackNested(Map<String,Object> brand) {
this.brandName = (String)brand.get("name");
Map<String,String> owner = (Map<String,String>)brand.get("owner");
this.ownerName = owner.get("name");
}
ownerName
being a field in the bean.
Now, I need to do something similar in Kotlin, but I am not happy with what I have so far. Assuming I have a MyPojo
class that has a createdAt
field, but in the JSON that represents it, the field is nested under a metadata
attribute:
data class MyPojo(var createdAt: LocalDateTime = LocalDateTime.MIN) {
@JsonProperty("metadata")
private fun unpackNested(metadata: Map<String, Any>) {
var createdAtAsString = metadata["createdAt"] as String
this.createdAt = LocalDateTime.parse(createdAtAsString,DateTimeFormatter.ISO_DATE_TIME)
}
}
One of the thing I don't like here is that I am forced to make createdAt
a var
, not a val
.
Is there a Kotlin trick to make things overall better here?
Upvotes: 0
Views: 3083
Reputation: 63
You can try to do this:
data class MyPojo(
var createdAt: LocalDateTime = LocalDateTime.MIN,
@JsonProperty("metadata")
private val metadata: Map<String, Any>
) {
init {
val createdAtAsString = metadata["createdAt"] as String
createdAt = LocalDateTime.parse(createdAtAsString, DateTimeFormatter.ISO_DATE_TIME)
}
}
or if you want createAt
to be val
, try this:
data class MyPojo(
@JsonProperty("metadata")
private val metadata: Map<String, Any>,
val createdAt: LocalDateTime = run {
val createdAtAsString = metadata["createdAt"] as String
LocalDateTime.parse(createdAtAsString, DateTimeFormatter.ISO_DATE_TIME)
}
)
Upvotes: 0
Reputation: 1
Pls check if this works for you
class JsonData() {
var createdAt by Delegates.notNull<Int>()
@JsonProperty("metadata")
private fun unpackNested(metadata: Map<String, Any>) {
createdAt = metadata["createdAt"] as Int
}
}
Upvotes: 0
Reputation: 29844
For the sake of simplicity, I used Int
as type for createdAt
.
You could do it like this:
class JsonData(createdAt: Int = 0) {
private var _createdAt: Int = createdAt
val createdAt: Int
get() = _createdAt
@JsonProperty("metadata")
private fun unpackNested(metadata: Map<String, Any>) {
_createdAt = metadata["createdAt"] as Int
}
}
createdAt
will be a parameter with a default value. Since a data classe's constructor can only have properties (var
/val
) you will loose the advantages of a data class (toString()
out of the box etc.).
You will assign this parameter to a private var _createdAt
when the class is instantiated.
The only thing that will be exposed to the outside is a property without a backing field createAt
(just a getter in Java terms). So, _createdAt
cannot be changed after instantiation.
There are two cases now:
_createdAt
will be set to the value you specify. _createdAt
will be overwritten by the unpackNested
call.Here is an example:
val jsonStr = """{
"metadata": {
"createdAt": 1
}
}
""".trimIndent()
fun main() {
val objectMapper = ObjectMapper()
// Jackson does instantiation
val jsonData = objectMapper.readValue(jsonStr, JsonData::class.java)
// you do it directly
JsonData(5)
}
Upvotes: 2