-
Notifications
You must be signed in to change notification settings - Fork 175
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
Support for inline classes #199
Comments
I use this workaround to read and write inline classes. inline class Watt(@JsonValue val value: Long)
@Test
fun `Read and write inline classes with Jackson`() {
val mapper = ObjectMapper().registerModule(KotlinModule()).registerModule(InlineModule)
val watt = Watt(1234)
val json = mapper.writeValueAsString(watt)
assertEquals("1234", json)
val result = mapper.readValue<Watt>(json)
assertEquals(watt, result)
}
object InlineModule : SimpleModule("Inline") {
override fun setupModule(context: SetupContext) {
super.setupModule(context)
context.appendAnnotationIntrospector(KotlinNamesAnnotationIntrospector)
}
object InlineAnnotationIntrospector : NopAnnotationIntrospector() {
override fun findCreatorAnnotation(config: MapperConfig<*>, a: Annotated): JsonCreator.Mode? {
if (a is AnnotatedMethod && a.name == "box-impl") {
return JsonCreator.Mode.DEFAULT
}
return null
}
}
} |
@TjeuKayim, is serialization working with inline classes now? |
I tried building the latest master branch, however the build fails. Instead I used 7bfb771. So inline classes are still not working in the latest release. |
Please do not use |
This would have to be studied to see how an inline class would be detected safely and without fail, I'm not sure if just the naming convention of the static constructor is clear enough. Maybe if detecting is Kotlin class, plus that naming convention, and a pattern of other methods maybe. |
I think this would be a pretty messy fix, but is worth looking into. @TjeuKayim did you abandon that PR completely? |
Unfortunately, I can't spend more time on this, and currently I'm not using Kotlin for any project. |
I used |
Has there been any updates to this? Or workaround code samples? |
No, I don't have any updates. The last few months I have not used Kotlin. For a workaround I refer to my comment above #199 (comment). |
@TjeuKayim thanks. The greater issue i have found is: #187. Once using the inline within a class, then it seems to fall apart on deserialization. |
Found a decent workaround today. Use Where @JsonDeserialize(builder = MyClassThatUsesAnInlineClassBuilder::class)
data class MyClassThatUsesAnInlineClass(val foo: String, val bar: MyInlineClass)
class MyClassThatUsesAnInlineClassBuilder {
lateinit var foo: String
lateinit var bar: String
fun withFoo(foo: String) = apply { this.foo = foo }
fun withBar(bar: String) = apply { this.bar = bar }
fun build() = MyClassThatUsesAnInlineClass(foo, MyInlineClass(bar))
}
|
Kotlin provides so called „Inline classes“ (https://kotlinlang.org/docs/reference/inline-classes.html), wrapping standard types into a dedicated type. This makes them a nice fit for IDs etc. The object mapping process works ootb, the parameter mapping needs some extra work: One has to provide custom serializers for all data classes being used (see `IdTypes`). Those serializers can be registered with a dedicated `SimpleModule`. This module needs to be added to the `ObjectMapper` instance used by OGM to convert parameters: ``` ObjectMapperFactory.objectMapper() .registerModule(KotlinModule()) .registerModule(IdTypesModule()) ``` The kotlin jackson module may detect them at some point in the future itself (See FasterXML/jackson-module-kotlin#199), but until than that work is necessary. This closes #822.
Kotlin provides so called „Inline classes“ (https://kotlinlang.org/docs/reference/inline-classes.html), wrapping standard types into a dedicated type. This makes them a nice fit for IDs etc. The object mapping process works ootb, the parameter mapping needs some extra work: One has to provide custom serializers for all data classes being used (see `IdTypes`). Those serializers can be registered with a dedicated `SimpleModule`. This module needs to be added to the `ObjectMapper` instance used by OGM to convert parameters: ``` ObjectMapperFactory.objectMapper() .registerModule(KotlinModule()) .registerModule(IdTypesModule()) ``` The kotlin jackson module may detect them at some point in the future itself (See FasterXML/jackson-module-kotlin#199), but until than that work is necessary. This closes #822.
Kotlin provides so called „Inline classes“ (https://kotlinlang.org/docs/reference/inline-classes.html), wrapping standard types into a dedicated type. This makes them a nice fit for IDs etc. The object mapping process works ootb, the parameter mapping needs some extra work: One has to provide custom serializers for all data classes being used (see `IdTypes`). Those serializers can be registered with a dedicated `SimpleModule`. This module needs to be added to the `ObjectMapper` instance used by OGM to convert parameters: ``` ObjectMapperFactory.objectMapper() .registerModule(KotlinModule()) .registerModule(IdTypesModule()) ``` The kotlin jackson module may detect them at some point in the future itself (See FasterXML/jackson-module-kotlin#199), but until than that work is necessary. This closes #822.
Kotlin provides so called „Inline classes“ (https://kotlinlang.org/docs/reference/inline-classes.html), wrapping standard types into a dedicated type. This makes them a nice fit for IDs etc. The object mapping process works ootb, the parameter mapping needs some extra work: One has to provide custom serializers for all data classes being used (see `IdTypes`). Those serializers can be registered with a dedicated `SimpleModule`. This module needs to be added to the `ObjectMapper` instance used by OGM to convert parameters: ``` ObjectMapperFactory.objectMapper() .registerModule(KotlinModule()) .registerModule(IdTypesModule()) ``` The kotlin jackson module may detect them at some point in the future itself (See FasterXML/jackson-module-kotlin#199), but until than that work is necessary. This closes #822.
Another possible way would be to use JsonCreator: inline class InlineType(val value: String)
data class ContainsInlineType(val inlineType: InlineType) {
companion object {
@JsonCreator
@JvmStatic
fun create(inlineType: String) = ContainsInlineType(InlineType(inlineType))
}
} |
@TjeuKayim Info: Your InlineAnnotationIntrospector Workaround does not seem to work with Kotlin 1.5. "box-impl" is now synthetic and jackson does not call the Introspector for it... JsonCreators or Builder would not be a workaround for me either because they have to be added to every class that uses the inline class... This clutters all DTOs... |
@k163377 Do you have a plan for |
@Quantum64 As far as I have researched, I think the following two points are relatively easy to achieve.
However, there are still some unresolved issues regarding the deserialization of p.s. |
I would like to try and take a stab at this, but I think I need a bit of guidance. @k163377 you mentioned
Do we have tests or examples that could give some coverage of those cases? Also those PRs to be merged before make it harder for a first time contributor to understand the scope of changes, would those be a requirement or nice to haves? |
Below is a summary of as much as I can remember of the work that needs to be done and the patterns that need to be covered, but I'm not sure if I've covered it all. Required work
Note that ideally, Patterns that need to be coveredAll combinations of the following patterns must be covered. If the value of
If the parameter on the
Also note that ideally, options such as I am very busy these days and have no time to work on this problem. |
I have achieved partial support for deserialization by functions containing Snapshots are available from However, this project does not use
I would appreciate a star to keep me motivated. |
I noticed that there is no explicit specification (at least not that I am aware of) for handling In jackson-module-kogera, the basic specification is to treat @JvmInline
value class Value(val value: Int)
val mapper = jacksonObjectMapper()
val json = mapper.writeValueAsString(Value(1)) // -> 1
val value = mapper.readValue<Value>(json) // -> Value(value=1) The reason for this specification is as follows.
Also, this specification seemed the easiest to implement due to the constraints of handling I plan to use the same specification when support |
Issues related to deserialization support for |
Hi, is there any updates? Thanks! |
Many use cases for serialization are already supported. For deserialization, I hope to have some support by 2.16. |
For the topic of |
Issue submitted on improving serialization performance of |
crossposting |
Deserialization of An RC version will be available soon, so please give it a try. This issue is closed, please submit a new issue if you have any problems. |
@k163377 Whoa! This sounds really great -- thank you for pushing it through. |
I created an issue for supporting value classes as map keys: #777 |
Kotlin 1.3 has experimental inline classes.
When wrapping a primitive type, an inline class instance is just compiled to the primitive type, making the code very efficient, whie still keeping it typesafe and preventing for example to sum Watts with Volts.
When the inline class instance is nullable, a wrapper class is used (just like java.lang.Integer is used to represent a nullable Int).
Unfortunately, inline classes don't follow the same rules as standard wrapper classes. Example:
The output of this program is:
It would be really nice if the first output was identical to the second one, and of course if unmarshalling worked too.
The text was updated successfully, but these errors were encountered: