Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to deserialize a kotlin.ranges.ClosedRange<T> with Jackson #663

Open
dibog opened this issue Apr 9, 2023 · 2 comments · Fixed by ProjectMapK/jackson-module-kogera#97

Comments

@dibog
Copy link

dibog commented Apr 9, 2023

Describe the bug
I would like to deserialize a data structure which contains a serialized ClosedRage field.

To Reproduce
Run this test case:

class ClosedRangeTest {
    data class TestClass(private val range: ClosedRange<Instant>)

    @Test
    fun closedRange() {
        val om = jacksonObjectMapper()
        val expected = TestClass(Instant.parse("2023-01-01T10:00:00Z")..Instant.parse("2023-01-01T11:00:00Z"))
        val json = om.writeValueAsString(expected)
        val actual = om.readValue<TestClass>(json)

        assert(actual == expected)
    }
}

Expected behavior
The test case should succeed and not to fail with:

Cannot construct instance of `kotlin.ranges.ClosedRange` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (String)"{}"; line: 1, column: 2]
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `kotlin.ranges.ClosedRange` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (String)"{}"; line: 1, column: 2]
	at app//com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
	at app//com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1909)
	at app//com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:408)
        ... many more lines ...

Versions
Kotlin: 1.7.22
Jackson-module-kotlin: 2.14.2
Jackson-databind: 2.14.2

@dibog dibog added the bug label Apr 9, 2023
@dibog dibog changed the title Hot to deserialize a kotlin.ranges.ClosedRange<T> with Jackson How to deserialize a kotlin.ranges.ClosedRange<T> with Jackson Apr 12, 2023
@k163377 k163377 added enhancement and removed bug labels Apr 21, 2023
@k163377
Copy link
Contributor

k163377 commented Apr 21, 2023

At this time, deserialization of Range other than integer (floating point or general Comparable) is not supported.

From what little I have tried, it seems that the simplest policy for the current implementation is to implement KNAI::refineDeserializationType.
However, because Kotlin does not expose an implementation class for ClosedRange, it depends on the private class.
https://youtrack.jetbrains.com/issue/KT-55376

It is theoretically possible to implement a deserializer for each of the internal types, but this seemed to get rather complicated, especially for the common Comparable (any PR would be welcome).

@k163377
Copy link
Contributor

k163377 commented Apr 21, 2023

upd: Although it still relies on private classes, I have come up with an even clearer implementation.
ProjectMapK/jackson-module-kogera#139


I am sharing a sample implementation of the changes I have made in a personal experimental project.
https://github.com/ProjectMapK/jackson-module-kogera/pull/97/files#diff-297e99ff3fd4616356c715949fe2e4294cd93d9cce43d31461432e52b3f7de63

For example, by creating and registering a NopAnnotationIntrospector that incorporates these processes, you can deserialize a generic ClosedRange.
However, this implementation depends on the private class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants