From f0aa1118e9ef66ed324f9a63cdfb551cb4e9eca5 Mon Sep 17 00:00:00 2001 From: Ori Schwartz Date: Mon, 18 Sep 2017 02:46:52 -0400 Subject: [PATCH] Java protobuf uses lower camel for all field names. When using reflection to find the generic type of repeated fields, can't use the user specified formats for field name conversion. (#1119) --- .../gson/protobuf/ProtoTypeAdapter.java | 23 ++++++++------ proto/src/main/protobuf/bag.proto | 5 +++ ...rotosWithComplexAndRepeatedFieldsTest.java | 31 +++++++++++++++++++ 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java b/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java index 5570edc9..0a137342 100644 --- a/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java +++ b/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java @@ -19,7 +19,6 @@ package com.google.gson.protobuf; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.CaseFormat; -import com.google.common.base.Converter; import com.google.common.collect.MapMaker; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; @@ -94,7 +93,8 @@ public class ProtoTypeAdapter private final Set> serializedNameExtensions; private final Set> serializedEnumValueExtensions; private EnumSerialization enumSerialization; - private Converter fieldNameSerializationFormat; + private CaseFormat protoFormat; + private CaseFormat jsonFormat; private Builder(EnumSerialization enumSerialization, CaseFormat fromFieldNameFormat, CaseFormat toFieldNameFormat) { @@ -126,7 +126,8 @@ public class ProtoTypeAdapter */ public Builder setFieldNameSerializationFormat(CaseFormat fromFieldNameFormat, CaseFormat toFieldNameFormat) { - fieldNameSerializationFormat = fromFieldNameFormat.converterTo(toFieldNameFormat); + this.protoFormat = fromFieldNameFormat; + this.jsonFormat = toFieldNameFormat; return this; } @@ -174,7 +175,7 @@ public class ProtoTypeAdapter } public ProtoTypeAdapter build() { - return new ProtoTypeAdapter(enumSerialization, fieldNameSerializationFormat, + return new ProtoTypeAdapter(enumSerialization, protoFormat, jsonFormat, serializedNameExtensions, serializedEnumValueExtensions); } } @@ -195,16 +196,19 @@ public class ProtoTypeAdapter new MapMaker().makeMap(); private final EnumSerialization enumSerialization; - private final Converter fieldNameSerializationFormat; + private final CaseFormat protoFormat; + private final CaseFormat jsonFormat; private final Set> serializedNameExtensions; private final Set> serializedEnumValueExtensions; private ProtoTypeAdapter(EnumSerialization enumSerialization, - Converter fieldNameSerializationFormat, + CaseFormat protoFormat, + CaseFormat jsonFormat, Set> serializedNameExtensions, Set> serializedEnumValueExtensions) { this.enumSerialization = enumSerialization; - this.fieldNameSerializationFormat = fieldNameSerializationFormat; + this.protoFormat = protoFormat; + this.jsonFormat = jsonFormat; this.serializedNameExtensions = serializedNameExtensions; this.serializedEnumValueExtensions = serializedEnumValueExtensions; } @@ -284,8 +288,9 @@ public class ProtoTypeAdapter protoBuilder.setField(fieldDescriptor, fieldValue); } else if (fieldDescriptor.isRepeated()) { // If the type is an array, then we have to grab the type from the class. + // protobuf java field names are always lower camel case String protoArrayFieldName = - fieldNameSerializationFormat.convert(fieldDescriptor.getName()) + "_"; + protoFormat.to(CaseFormat.LOWER_CAMEL, fieldDescriptor.getName()) + "_"; Field protoArrayField = protoClass.getDeclaredField(protoArrayFieldName); Type protoArrayFieldType = protoArrayField.getGenericType(); fieldValue = context.deserialize(jsonElement, protoArrayFieldType); @@ -325,7 +330,7 @@ public class ProtoTypeAdapter return options.getExtension(extension); } } - return fieldNameSerializationFormat.convert(defaultName); + return protoFormat.to(jsonFormat, defaultName); } /** diff --git a/proto/src/main/protobuf/bag.proto b/proto/src/main/protobuf/bag.proto index 26189a24..48cc9639 100644 --- a/proto/src/main/protobuf/bag.proto +++ b/proto/src/main/protobuf/bag.proto @@ -24,6 +24,11 @@ message SimpleProto { optional int32 count = 2; } +message ProtoWithDifferentCaseFormat { + repeated string name_that_tests_case_format = 1; + optional string another_field = 2; +} + message ProtoWithRepeatedFields { repeated int64 numbers = 1; repeated SimpleProto simples = 2; diff --git a/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithComplexAndRepeatedFieldsTest.java b/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithComplexAndRepeatedFieldsTest.java index 964549d6..b61d3f50 100644 --- a/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithComplexAndRepeatedFieldsTest.java +++ b/proto/src/test/java/com/google/gson/protobuf/functional/ProtosWithComplexAndRepeatedFieldsTest.java @@ -15,10 +15,13 @@ */ package com.google.gson.protobuf.functional; +import com.google.common.base.CaseFormat; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; import com.google.gson.protobuf.ProtoTypeAdapter; import com.google.gson.protobuf.ProtoTypeAdapter.EnumSerialization; +import com.google.gson.protobuf.generated.Bag.ProtoWithDifferentCaseFormat; import com.google.gson.protobuf.generated.Bag.ProtoWithRepeatedFields; import com.google.gson.protobuf.generated.Bag.SimpleProto; import com.google.protobuf.GeneratedMessage; @@ -32,6 +35,7 @@ import junit.framework.TestCase; */ public class ProtosWithComplexAndRepeatedFieldsTest extends TestCase { private Gson gson; + private Gson upperCamelGson; @Override protected void setUp() throws Exception { @@ -43,6 +47,14 @@ public class ProtosWithComplexAndRepeatedFieldsTest extends TestCase { .setEnumSerialization(EnumSerialization.NUMBER) .build()) .create(); + upperCamelGson = + new GsonBuilder() + .registerTypeHierarchyAdapter( + GeneratedMessage.class, ProtoTypeAdapter.newBuilder() + .setFieldNameSerializationFormat( + CaseFormat.LOWER_UNDERSCORE, CaseFormat.UPPER_CAMEL) + .build()) + .create(); } public void testSerializeRepeatedFields() { @@ -67,4 +79,23 @@ public class ProtosWithComplexAndRepeatedFieldsTest extends TestCase { assertEquals("bar", proto.getSimples(0).getMsg()); assertEquals(7, proto.getSimples(1).getCount()); } + + public void testSerializeDifferentCaseFormat() { + final ProtoWithDifferentCaseFormat proto = + ProtoWithDifferentCaseFormat.newBuilder() + .setAnotherField("foo") + .addNameThatTestsCaseFormat("bar") + .build(); + final JsonObject json = upperCamelGson.toJsonTree(proto).getAsJsonObject(); + assertEquals("foo", json.get("AnotherField").getAsString()); + assertEquals("bar", json.get("NameThatTestsCaseFormat").getAsJsonArray().get(0).getAsString()); + } + + public void testDeserializeDifferentCaseFormat() { + final String json = "{NameThatTestsCaseFormat:['bar'],AnotherField:'foo'}"; + ProtoWithDifferentCaseFormat proto = + upperCamelGson.fromJson(json, ProtoWithDifferentCaseFormat.class); + assertEquals("foo", proto.getAnotherField()); + assertEquals("bar", proto.getNameThatTestsCaseFormat(0)); + } }