Skip to content

Commit 42ca7a6

Browse files
committed
[ECO-5386] Implement JSON and MessagePack serialization for ObjectMessage
1. Added jackson-dataformat-msgpack 0.8.11 dependency for automatic msgpack handling 2. Implemented JSON serialization via ObjectMessage.toJsonObject() and JsonObject.toObjectMessage() extensions 3. Implemented MessagePack serialization via ObjectMessage.writeTo() and MessageUnpacker.readObjectMessage() extensions 4. Added @SerializedName("object") annotation for proper field naming consistency
1 parent d227718 commit 42ca7a6

File tree

4 files changed

+78
-10
lines changed

4 files changed

+78
-10
lines changed

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mockk = "1.14.2"
2525
turbine = "1.2.0"
2626
ktor = "3.1.3"
2727
jetbrains-annoations = "26.0.2"
28+
jackson-msgpack = "0.8.11" # Compatible with msgpack-core 0.8.11
2829

2930
[libraries]
3031
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
@@ -54,6 +55,7 @@ turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine
5455
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
5556
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
5657
jetbrains = { group = "org.jetbrains", name = "annotations", version.ref = "jetbrains-annoations" }
58+
jackson-msgpack = { group = "org.msgpack", name = "jackson-dataformat-msgpack", version.ref = "jackson-msgpack" }
5759

5860
[bundles]
5961
common = ["msgpack", "vcdiff-core"]

live-objects/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies {
1313
implementation(project(":java"))
1414
implementation(libs.bundles.common)
1515
implementation(libs.coroutine.core)
16+
implementation(libs.jackson.msgpack)
1617

1718
testImplementation(kotlin("test"))
1819
testImplementation(libs.bundles.kotlin.tests)

live-objects/src/main/kotlin/io/ably/lib/objects/ObjectMessage.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.ably.lib.objects
22

3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
import com.google.gson.annotations.SerializedName
5+
36
/**
47
* An enum class representing the different actions that can be performed on an object.
58
* Spec: OOP2
@@ -318,6 +321,8 @@ internal data class ObjectMessage(
318321
* the `ProtocolMessage` encapsulating it is `OBJECT_SYNC`.
319322
* Spec: OM2g
320323
*/
324+
@SerializedName("object")
325+
@JsonProperty("object")
321326
val objectState: ObjectState? = null,
322327

323328
/**
Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,91 @@
1+
@file:Suppress("UNCHECKED_CAST")
2+
13
package io.ably.lib.objects
24

3-
import com.google.gson.Gson
4-
import com.google.gson.GsonBuilder
5-
import com.google.gson.JsonArray
5+
import com.fasterxml.jackson.databind.ObjectMapper
6+
import com.google.gson.*
7+
import org.msgpack.core.MessagePack
68
import org.msgpack.core.MessagePacker
79
import org.msgpack.core.MessageUnpacker
10+
import org.msgpack.jackson.dataformat.MessagePackFactory
11+
12+
// Gson instance for JSON serialization/deserialization
13+
internal val gson: Gson = GsonBuilder().create()
14+
15+
// Jackson ObjectMapper for MessagePack serialization (respects @JsonProperty annotations)
16+
private val msgpackMapper = ObjectMapper(MessagePackFactory())
17+
18+
internal fun ObjectMessage.toJsonObject(): JsonObject {
19+
return gson.toJsonTree(this).asJsonObject
20+
}
21+
22+
internal fun JsonObject.toObjectMessage(): ObjectMessage {
23+
return gson.fromJson(this, ObjectMessage::class.java)
24+
}
25+
26+
internal fun ObjectMessage.writeTo(packer: MessagePacker) {
27+
// Jackson automatically creates the correct msgpack map structure
28+
val msgpackBytes = msgpackMapper.writeValueAsBytes(this)
29+
30+
// Parse the msgpack bytes to get the structured value
31+
val tempUnpacker = MessagePack.newDefaultUnpacker(msgpackBytes)
32+
val msgpackValue = tempUnpacker.unpackValue()
33+
tempUnpacker.close()
34+
35+
// Pack the structured value using the provided packer
36+
packer.packValue(msgpackValue)
37+
}
38+
39+
internal fun MessageUnpacker.readObjectMessage(): ObjectMessage {
40+
// Read the msgpack value from the unpacker
41+
val msgpackValue = this.unpackValue()
842

9-
internal val gson: Gson = createGsonSerializer()
43+
// Convert the msgpack value back to bytes
44+
val tempPacker = MessagePack.newDefaultBufferPacker()
45+
tempPacker.packValue(msgpackValue)
46+
val msgpackBytes = tempPacker.toByteArray()
47+
tempPacker.close()
1048

11-
private fun createGsonSerializer(): Gson {
12-
return GsonBuilder().create() // Do not call serializeNulls() to omit null values
49+
// Let Jackson deserialize the msgpack bytes back to ObjectMessage
50+
return msgpackMapper.readValue(msgpackBytes, ObjectMessage::class.java)
1351
}
1452

1553
internal class DefaultLiveObjectSerializer : LiveObjectSerializer {
1654
override fun readMsgpackArray(unpacker: MessageUnpacker): Array<Any> {
17-
TODO("Not yet implemented")
55+
val objectMessagesCount = unpacker.unpackArrayHeader()
56+
val objectMessages = mutableListOf<ObjectMessage>()
57+
for (i in 0 until objectMessagesCount) {
58+
objectMessages.add(unpacker.readObjectMessage())
59+
}
60+
return objectMessages.toTypedArray()
1861
}
1962

2063
override fun writeMsgpackArray(objects: Array<out Any>?, packer: MessagePacker) {
21-
TODO("Not yet implemented")
64+
val objectMessages: Array<ObjectMessage> = objects as Array<ObjectMessage>
65+
packer.packArrayHeader(objectMessages.size)
66+
for (objectMessage in objectMessages) {
67+
objectMessage.writeTo(packer)
68+
}
2269
}
2370

2471
override fun readFromJsonArray(json: JsonArray): Array<Any> {
25-
TODO("Not yet implemented")
72+
val objectMessages = mutableListOf<ObjectMessage>()
73+
for (element in json) {
74+
if (element.isJsonObject) {
75+
objectMessages.add(element.asJsonObject.toObjectMessage())
76+
} else {
77+
throw JsonParseException("Expected JsonObject, but found: $element")
78+
}
79+
}
80+
return objectMessages.toTypedArray()
2681
}
2782

2883
override fun asJsonArray(objects: Array<out Any>?): JsonArray {
29-
TODO("Not yet implemented")
84+
val objectMessages: Array<ObjectMessage> = objects as Array<ObjectMessage>
85+
val jsonArray = JsonArray()
86+
for (objectMessage in objectMessages) {
87+
jsonArray.add(objectMessage.toJsonObject())
88+
}
89+
return jsonArray
3090
}
3191
}

0 commit comments

Comments
 (0)