Skip to content

Commit

Permalink
Fix serializing for map with google.protobuf.Any value (#43)
Browse files Browse the repository at this point in the history
* Fix serializing for map with google.protobuf.Any value

* Make ktlint happy
  • Loading branch information
devkanro authored Jul 9, 2020
1 parent 68e14a4 commit ba3aaab
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.bybutter.sisyphus.protobuf.jackson

import com.bybutter.sisyphus.jackson.javaType
import com.bybutter.sisyphus.protobuf.CustomProtoType
import com.bybutter.sisyphus.protobuf.Message
import com.bybutter.sisyphus.protobuf.ProtoEnum
import com.bybutter.sisyphus.protobuf.WellKnownTypes
import com.bybutter.sisyphus.protobuf.primitives.BoolValue
import com.bybutter.sisyphus.protobuf.primitives.BytesValue
import com.bybutter.sisyphus.protobuf.primitives.DoubleValue
Expand All @@ -28,9 +28,11 @@ import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import com.fasterxml.jackson.databind.type.TypeFactory
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator
import java.lang.reflect.Type
import kotlin.math.round
import kotlin.reflect.jvm.javaType

@OptIn(ExperimentalUnsignedTypes::class)
open class ProtoSerializer<T : Message<*, *>> : StdSerializer<T> {
Expand All @@ -43,7 +45,7 @@ open class ProtoSerializer<T : Message<*, *>> : StdSerializer<T> {
constructor(type: JavaType) : super(type)

override fun serialize(value: T, gen: JsonGenerator, provider: SerializerProvider) {
writeAny(value as Any, gen, provider)
writeAny(value as Any, value.javaType, gen, provider)
}

private fun getSerializedPropertyName(field: FieldDescriptorProto, gen: JsonGenerator, provider: SerializerProvider): String {
Expand All @@ -60,19 +62,11 @@ open class ProtoSerializer<T : Message<*, *>> : StdSerializer<T> {
}

gen.writeFieldName(getSerializedPropertyName(field, gen, provider))
if (field.typeName == WellKnownTypes.ANY_TYPENAME) {
if (fieldValue is List<*>) {
writeAnyList(fieldValue as List<Message<*, *>>, gen, provider)
} else {
writeAny(fieldValue as Message<*, *>, gen, provider)
}
} else {
writeAny(fieldValue!!, gen, provider)
}
writeAny(fieldValue!!, TypeFactory.defaultInstance().constructType(value.getProperty(field.number)?.returnType?.javaType), gen, provider)
}
}

protected fun writeAny(value: Any, gen: JsonGenerator, provider: SerializerProvider) {
protected fun writeAny(value: Any, type: JavaType, gen: JsonGenerator, provider: SerializerProvider) {
when (value) {
is NullValue -> writeNull(value, gen, provider)
is String -> gen.writeString(value)
Expand All @@ -96,10 +90,16 @@ open class ProtoSerializer<T : Message<*, *>> : StdSerializer<T> {
is ListValue -> writeList(value, gen, provider)
is FieldMask -> writeFieldMask(value, gen, provider)
is com.bybutter.sisyphus.protobuf.primitives.Any -> writeAny(value.toMessage(), gen, provider)
is Message<*, *> -> writeRawProto(value, gen, provider)
is List<*> -> writeList(value, type, gen, provider)
is Map<*, *> -> writeMap(value, type, gen, provider)
is CustomProtoType<*> -> writeCustom(value, gen, provider)
is List<*> -> writeList(value, gen, provider)
is Map<*, *> -> writeMap(value, gen, provider)
is Message<*, *> -> {
if (type.rawClass == Message::class.java) {
writeAny(value as Message<*, *>, gen, provider)
} else {
writeRawProto(value, gen, provider)
}
}
else -> {
throw UnsupportedOperationException("Unsupported type '${value.javaClass}($value)' in proto.")
}
Expand All @@ -113,7 +113,8 @@ open class ProtoSerializer<T : Message<*, *>> : StdSerializer<T> {
}

protected fun writeCustom(value: CustomProtoType<*>, gen: JsonGenerator, provider: SerializerProvider) {
writeAny(value.raw() ?: NullValue.NULL_VALUE, gen, provider)
val raw = value.raw() ?: NullValue.NULL_VALUE
writeAny(raw, raw.javaType, gen, provider)
}

protected fun writeAnyList(value: List<Message<*, *>>, gen: JsonGenerator, provider: SerializerProvider) {
Expand Down Expand Up @@ -300,22 +301,22 @@ open class ProtoSerializer<T : Message<*, *>> : StdSerializer<T> {
gen.writeEndArray()
}

protected fun writeList(value: List<*>, gen: JsonGenerator, provider: SerializerProvider) {
protected fun writeList(value: List<*>, type: JavaType, gen: JsonGenerator, provider: SerializerProvider) {
gen.writeStartArray()
for (item in value) {
item ?: continue
writeAny(item, gen, provider)
writeAny(item, type.contentType, gen, provider)
}
gen.writeEndArray()
}

protected fun writeMap(value: Map<*, *>, gen: JsonGenerator, provider: SerializerProvider) {
protected fun writeMap(value: Map<*, *>, type: JavaType, gen: JsonGenerator, provider: SerializerProvider) {
gen.writeStartObject()
for ((key, value) in value) {
key ?: continue
value ?: continue
gen.writeFieldName(key.toString())
writeAny(value, gen, provider)
writeAny(value, type.contentType, gen, provider)
}
gen.writeEndObject()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.bybutter.sisyphus.protobuf.ProtoTypes
* Wrap message to [Any].
*/
fun Message<*, *>.toAny(): Any {
if (this is Any) return this
return Any {
this.typeUrl = this@toAny.typeUrl()
this.value = this@toAny.toProto()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,22 @@ class ProtoTest {
this.oneTest = MapMessageTest.OneTest.StringOneofValue("test")
this.timestamp = Timestamp.now()
this.duration = Duration(8L, 0L, 0L)
this.anyMapValue += mapOf(
"foo" to MapMessageTest.NestedMessage {
this.int32Value = 1
},
"bar" to PackedTest {
this.values += listOf(1, 2, 3, 4, 5, 6)
}
)
this.anyListValue += listOf(
MapMessageTest.NestedMessage {
this.int32Value = 1
},
PackedTest {
this.values += listOf(1, 2, 3, 4, 5, 6)
}
)
}

val json = raw.toJson()
Expand All @@ -147,6 +163,22 @@ class ProtoTest {
this.oneTest = MapMessageTest.OneTest.StringOneofValue("test")
this.timestamp = Timestamp.now()
this.duration = Duration(8L, 0L, 0L)
this.anyMapValue += mapOf(
"foo" to MapMessageTest.NestedMessage {
this.int32Value = 1
},
"bar" to PackedTest {
this.values += listOf(1, 2, 3, 4, 5, 6)
}
)
this.anyListValue += listOf(
MapMessageTest.NestedMessage {
this.int32Value = 1
},
PackedTest {
this.values += listOf(1, 2, 3, 4, 5, 6)
}
)
}

val yml = raw.toYaml()
Expand Down
3 changes: 3 additions & 0 deletions lib/sisyphus-protobuf/src/test/proto/test.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ syntax = "proto3";

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/any.proto";

package com.bybutter.sisyphus.protobuf.test;

Expand Down Expand Up @@ -31,6 +32,8 @@ message MapMessageTest {
}
google.protobuf.Timestamp timestamp = 7;
google.protobuf.Duration duration = 8;
map<string, google.protobuf.Any> any_map_value = 9;
repeated google.protobuf.Any any_list_value = 10;

message NestedMessage {
int32 int32_value = 1;
Expand Down

0 comments on commit ba3aaab

Please sign in to comment.