kotlinx.serialization icon indicating copy to clipboard operation
kotlinx.serialization copied to clipboard

[protobuf] How to encode/decode map with null value?

Open xiaozhikang0916 opened this issue 1 year ago • 0 comments

Describe the bug

Giving a simple proto message:

message ProtoMap {
  map<string, TypeValue> map = 1;
}

message TypeValue {
  int32 value = 1;
}

Our backend server in go may assign the map as key to null and send data like 0a050a036b6579 (1: {1: {"key"}} in readable string) with value absent.

But I found it impossible to define a valid class in represent to this message.

I tried:

@Serializable
data class ProtoMap(@ProtoNumber(1) val map: Map<String, TypeValue>)

to see if parser will give a default 0 in map, but failed to parse, and

@Serializable
data class ProtoMapNullable(@ProtoNumber(1) val map: Map<String, TypeValue?>)

This is good for decoding, but failing in encoding.

Is there any suggestion for such message, or it is a bug of map encoding?

PS I believe it is valid data to have null value in map, as described in Proto Spec.

To Reproduce

Run the test code below


package kotlinx.serialization

import kotlinx.serialization.protobuf.ProtoBuf
import kotlinx.serialization.protobuf.ProtoNumber
import kotlin.test.Test
import kotlin.test.assertEquals

@Serializable
data class TypeValue(@ProtoNumber(1) val int: Int = 0)
@Serializable
data class ProtoMap(@ProtoNumber(1) val map: Map<String, TypeValue>)
@Serializable
data class ProtoMapNullable(@ProtoNumber(1) val map: Map<String, TypeValue?>)

const val hexWithoutValue = "0a050a036b6579"

class MapTest {
    @Test
    fun testDecodeNullValueToNullableMap() {
        val value = hexWithoutValue
        val decoded = ProtoBuf.decodeFromHexString<ProtoMapNullable>(value)
        assertEquals(ProtoMapNullable(mapOf("key" to null)), decoded)
        val encode = ProtoBuf.encodeToHexString(decoded)
        println(encode)
    }

    @Test
    fun testDecodeNullValueToMap() {
        val value = hexWithoutValue
        val decoded = ProtoBuf.decodeFromHexString<ProtoMap>(value)
        assertEquals(ProtoMap(mapOf("key" to TypeValue(0))), decoded)
        val encode = ProtoBuf.encodeToHexString(decoded)
        println(encode)
    }
}

Expected behavior

One of the test should be passed, maybe?

Environment

  • Kotlin version: 2.0.0 and 1.9.23
  • Library version: 1.7.1 and 1.6.3
  • Kotlin platforms: JVM, JS, iOS

xiaozhikang0916 avatar Aug 06 '24 03:08 xiaozhikang0916