From 085768b41943237f50d5e47fa4ba5f22abff9fc6 Mon Sep 17 00:00:00 2001 From: dark0dave <52840419+dark0dave@users.noreply.github.com> Date: Tue, 29 Mar 2022 21:45:53 +0100 Subject: [PATCH] fix: Numerics cast from strings (#1588) Signed-off-by: dark0dave --- .../storage/v1/JsonToProtoMessage.java | 55 +++++++++++++++++++ .../storage/v1/JsonToProtoMessageTest.java | 22 +++++++- .../src/test/proto/jsonTest.proto | 9 +++ 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1/JsonToProtoMessage.java b/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1/JsonToProtoMessage.java index 668a447dbf..ff62838e0b 100644 --- a/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1/JsonToProtoMessage.java +++ b/google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1/JsonToProtoMessage.java @@ -18,6 +18,9 @@ import com.google.api.pathtemplate.ValidationException; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.common.primitives.Doubles; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; @@ -226,6 +229,12 @@ private static void fillField( protoMsg.setField(fieldDescriptor, (Boolean) val); return; } + if (val instanceof String + && ("true".equals(((String) val).toLowerCase()) + || "false".equals(((String) val).toLowerCase()))) { + protoMsg.setField(fieldDescriptor, Boolean.parseBoolean((String) val)); + return; + } break; case BYTES: if (fieldSchema != null) { @@ -312,6 +321,13 @@ private static void fillField( protoMsg.setField(fieldDescriptor, (Long) val); return; } + if (val instanceof String) { + Long parsed = Longs.tryParse((String) val); + if (parsed != null) { + protoMsg.setField(fieldDescriptor, parsed); + return; + } + } break; case INT32: if (fieldSchema != null && fieldSchema.getType() == TableFieldSchema.Type.DATE) { @@ -327,6 +343,13 @@ private static void fillField( protoMsg.setField(fieldDescriptor, (Integer) val); return; } + if (val instanceof String) { + Integer parsed = Ints.tryParse((String) val); + if (parsed != null) { + protoMsg.setField(fieldDescriptor, parsed); + return; + } + } break; case STRING: if (val instanceof String) { @@ -339,6 +362,13 @@ private static void fillField( protoMsg.setField(fieldDescriptor, ((Number) val).doubleValue()); return; } + if (val instanceof String) { + Double parsed = Doubles.tryParse((String) val); + if (parsed != null) { + protoMsg.setField(fieldDescriptor, parsed); + return; + } + } break; case MESSAGE: if (val instanceof JSONObject) { @@ -400,6 +430,10 @@ private static void fillRepeatedField( case BOOL: if (val instanceof Boolean) { protoMsg.addRepeatedField(fieldDescriptor, (Boolean) val); + } else if (val instanceof String + && ("true".equals(((String) val).toLowerCase()) + || "false".equals(((String) val).toLowerCase()))) { + protoMsg.addRepeatedField(fieldDescriptor, Boolean.parseBoolean((String) val)); } else { fail = true; } @@ -491,6 +525,13 @@ private static void fillRepeatedField( protoMsg.addRepeatedField(fieldDescriptor, new Long((Integer) val)); } else if (val instanceof Long) { protoMsg.addRepeatedField(fieldDescriptor, (Long) val); + } else if (val instanceof String) { + Long parsed = Longs.tryParse((String) val); + if (parsed != null) { + protoMsg.addRepeatedField(fieldDescriptor, parsed); + } else { + fail = true; + } } else { fail = true; } @@ -507,6 +548,13 @@ private static void fillRepeatedField( } } else if (val instanceof Integer) { protoMsg.addRepeatedField(fieldDescriptor, (Integer) val); + } else if (val instanceof String) { + Integer parsed = Ints.tryParse((String) val); + if (parsed != null) { + protoMsg.addRepeatedField(fieldDescriptor, parsed); + } else { + fail = true; + } } else { fail = true; } @@ -521,6 +569,13 @@ private static void fillRepeatedField( case DOUBLE: if (val instanceof Number) { protoMsg.addRepeatedField(fieldDescriptor, ((Number) val).doubleValue()); + } else if (val instanceof String) { + Double parsed = Doubles.tryParse((String) val); + if (parsed != null) { + protoMsg.addRepeatedField(fieldDescriptor, parsed); + } else { + fail = true; + } } else { fail = true; } diff --git a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1/JsonToProtoMessageTest.java b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1/JsonToProtoMessageTest.java index 7b5c6bcfad..90ff40c090 100644 --- a/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1/JsonToProtoMessageTest.java +++ b/google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/v1/JsonToProtoMessageTest.java @@ -473,15 +473,29 @@ public void testDifferentNameCasing() throws Exception { assertEquals(expectedProto, protoMsg); } + @Test + public void testBool() throws Exception { + TestBool expectedProto = + TestBool.newBuilder().setBool(true).setUppercase(true).setLowercase(false).build(); + JSONObject json = new JSONObject(); + json.put("bool", true); + json.put("uppercase", "TRUE"); + json.put("lowercase", "false"); + DynamicMessage protoMsg = + JsonToProtoMessage.convertJsonToProtoMessage(TestBool.getDescriptor(), json); + assertEquals(expectedProto, protoMsg); + } + @Test public void testInt64() throws Exception { TestInt64 expectedProto = - TestInt64.newBuilder().setByte(1).setShort(1).setInt(1).setLong(1).build(); + TestInt64.newBuilder().setByte(1).setShort(1).setInt(1).setLong(1).setString(1).build(); JSONObject json = new JSONObject(); json.put("byte", (byte) 1); json.put("short", (short) 1); json.put("int", 1); json.put("long", 1L); + json.put("string", "1"); DynamicMessage protoMsg = JsonToProtoMessage.convertJsonToProtoMessage(TestInt64.getDescriptor(), json); assertEquals(expectedProto, protoMsg); @@ -489,11 +503,13 @@ public void testInt64() throws Exception { @Test public void testInt32() throws Exception { - TestInt32 expectedProto = TestInt32.newBuilder().setByte(1).setShort(1).setInt(1).build(); + TestInt32 expectedProto = + TestInt32.newBuilder().setByte(1).setShort(1).setInt(1).setString(1).build(); JSONObject json = new JSONObject(); json.put("byte", (byte) 1); json.put("short", (short) 1); json.put("int", 1); + json.put("string", 1); DynamicMessage protoMsg = JsonToProtoMessage.convertJsonToProtoMessage(TestInt32.getDescriptor(), json); assertEquals(expectedProto, protoMsg); @@ -625,6 +641,7 @@ public void testDouble() throws Exception { .setShort(6) .setInt(7) .setLong(8) + .setString(9.1) .build(); JSONObject json = new JSONObject(); json.put("double", 1.2); @@ -633,6 +650,7 @@ public void testDouble() throws Exception { json.put("short", new Short((short) 6)); json.put("int", 7); json.put("long", 8L); + json.put("string", "9.1"); DynamicMessage protoMsg = JsonToProtoMessage.convertJsonToProtoMessage(TestDouble.getDescriptor(), json); assertEquals(expectedProto, protoMsg); diff --git a/google-cloud-bigquerystorage/src/test/proto/jsonTest.proto b/google-cloud-bigquerystorage/src/test/proto/jsonTest.proto index fc76467f81..6fea46f021 100644 --- a/google-cloud-bigquerystorage/src/test/proto/jsonTest.proto +++ b/google-cloud-bigquerystorage/src/test/proto/jsonTest.proto @@ -103,17 +103,25 @@ message RepeatedObject { repeated ComplexLvl2 test_repeated = 1; } +message TestBool { + optional bool bool = 1; + optional bool lowercase = 2; + optional bool uppercase = 3; +} + message TestInt64 { optional int64 byte = 1; optional int64 short = 2; optional int64 int = 3; optional int64 long = 4; + optional int64 string = 5; } message TestInt32 { optional int32 byte = 1; optional int32 short = 2; optional int32 int = 3; + optional int32 string = 4; } message TestDouble { @@ -123,6 +131,7 @@ message TestDouble { optional double short = 4; optional double int = 5; optional double long = 6; + optional double string = 7; } message TestDate {