Skip to content

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

Closed
@xiaozhikang0916

Description

@xiaozhikang0916

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions