Open InsanusMokrassar opened 5 years ago
So, next code will give the same effect:
@Serializable(ParentFlatChildSerializer::class)
data class Parent(
val stringField: String = "stringValue",
val flatField: Child = Child()
)
@Serializable
class Child {
@Optional
val flatFieldValue: String = "flatFieldValue"
}
@Serializer(Parent::class)
object ParentFlatChildSerializer : KSerializer<Parent> {
override fun serialize(output: Encoder, obj: Parent) {
output.beginStructure(
descriptor
).apply {
encodeStringElement(
descriptor, 0, obj.stringField
)
encodeStringElement(Child.serializer().descriptor, 0, obj.flatField.flatFieldValue)
}.endStructure(
descriptor
)
}
}
fun main(args: Array<String>) {
println(JSON.stringify(ParentFlatChildSerializer, Parent()))
}
But it is much longer and is not useful to write serializer like this for each object/class in projects.
Any information about this? Is there some workaround, or will it be implemented in near future?
This would be nice to have.
I join the queue to request this feature. It would be extremely useful indeed. Thank you!
Same here
There are two workarounds. One is inline classes (or tricking the serialization framework into thinking the class is inline - using a custom SerialDescriptor
). The other is to use a custom serializer. If you give its descriptor as PrimitiveKind.String (there is a factory function for primitive descriptors, not even "internal"), then you can have the implementation just use encodeString, decodeString and do the "right" translation to get the objects.
What isn't really possible is to embed multi-attribute members into a parent (neither way really works), and that would require quite some changes on the compiler plugin side.
Rusts serde has a great impl of this, could be of use when designing the API if its pursued
Not the best solution, but a bit better than writing a full serializer. Uses JsonTransformingSerializer
:
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonTransformingSerializer
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonObject
@Serializable
data class Parent(
val stringField: String = "stringValue",
val flatField: Child = Child()
)
@Serializable
class Child {
val childString: String = "childValue"
val otherChildString: String = "otherChildVale"
}
object UnwrappingJsonSerializer: JsonTransformingSerializer<Parent>(Parent.serializer()) {
override fun transformSerialize(element: JsonElement) = buildJsonObject {
element.jsonObject.forEach { (propertyName, propertyValue) ->
if (propertyName == Parent::flatField.name) {
propertyValue.jsonObject.forEach(::put)
} else {
put(propertyName, propertyValue)
}
}
}
}
fun main(args: Array<String>) {
println(
Json {
prettyPrint = true
encodeDefaults = true
}.encodeToString(
serializer = UnwrappingJsonSerializer,
value = Parent()
)
)
}
will print:
{
"stringField": "stringValue",
"childString": "childValue",
"otherChildString": "otherChildVale"
}
AndroidX Room calls this @Embedded
. There's another issue with the same goal here https://github.com/Kotlin/kotlinx.serialization/issues/1734
I offer to include
@Flat
(or@Flatten
, for example) annotation which will be used in cases when value object of field must be included directly into parent object.Will give result:
Here I see a few problems:
String
and other primitives must be resolved like common fields (with their keys) or those fields must lead to exception likeIllegalArgumentException