Skip to content

Commit a471f1e

Browse files
authored
feat: add support for BigDecimal to CustomClassMapper (#196)
1 parent 1e05de4 commit a471f1e

File tree

3 files changed

+109
-7
lines changed

3 files changed

+109
-7
lines changed

google-cloud-firestore/src/main/java/com/google/cloud/firestore/CustomClassMapper.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.lang.reflect.Type;
3636
import java.lang.reflect.TypeVariable;
3737
import java.lang.reflect.WildcardType;
38+
import java.math.BigDecimal;
3839
import java.util.ArrayList;
3940
import java.util.Collection;
4041
import java.util.Collections;
@@ -117,11 +118,13 @@ private static <T> Object serialize(T o, ErrorPath path) {
117118
} else if (o instanceof Number) {
118119
if (o instanceof Long || o instanceof Integer || o instanceof Double || o instanceof Float) {
119120
return o;
121+
} else if (o instanceof BigDecimal) {
122+
return String.valueOf(o);
120123
} else {
121124
throw serializeError(
122125
path,
123126
String.format(
124-
"Numbers of type %s are not supported, please use an int, long, float or double",
127+
"Numbers of type %s are not supported, please use an int, long, float, double or BigDecimal",
125128
o.getClass().getSimpleName()));
126129
}
127130
} else if (o instanceof String) {
@@ -324,6 +327,8 @@ private static <T> T deserializeToPrimitive(
324327
return (T) convertDouble(o, context);
325328
} else if (Long.class.isAssignableFrom(clazz) || long.class.isAssignableFrom(clazz)) {
326329
return (T) convertLong(o, context);
330+
} else if (BigDecimal.class.isAssignableFrom(clazz)) {
331+
return (T) convertBigDecimal(o, context);
327332
} else if (Float.class.isAssignableFrom(clazz) || float.class.isAssignableFrom(clazz)) {
328333
return (T) (Float) convertDouble(o, context).floatValue();
329334
} else {
@@ -462,6 +467,24 @@ private static Double convertDouble(Object o, DeserializeContext context) {
462467
}
463468
}
464469

470+
private static BigDecimal convertBigDecimal(Object o, DeserializeContext context) {
471+
if (o instanceof Integer) {
472+
return BigDecimal.valueOf(((Integer) o).intValue());
473+
} else if (o instanceof Long) {
474+
return BigDecimal.valueOf(((Long) o).longValue());
475+
} else if (o instanceof Double) {
476+
return BigDecimal.valueOf(((Double) o).doubleValue()).abs();
477+
} else if (o instanceof BigDecimal) {
478+
return (BigDecimal) o;
479+
} else if (o instanceof String) {
480+
return new BigDecimal((String) o);
481+
} else {
482+
throw deserializeError(
483+
context.errorPath,
484+
"Failed to convert a value of type " + o.getClass().getName() + " to BigDecimal");
485+
}
486+
}
487+
465488
private static Boolean convertBoolean(Object o, DeserializeContext context) {
466489
if (o instanceof Boolean) {
467490
return (Boolean) o;

google-cloud-firestore/src/test/java/com/google/cloud/firestore/DocumentReferenceTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,19 +180,19 @@ public void doesNotSerializeAdvancedNumberTypes() {
180180
pojo.bigIntegerValue = new BigInteger("0");
181181
expectedErrorMessages.put(
182182
pojo,
183-
"Could not serialize object. Numbers of type BigInteger are not supported, please use an int, long, float or double (found in field 'bigIntegerValue')");
183+
"Could not serialize object. Numbers of type BigInteger are not supported, please use an int, long, float, double or BigDecimal (found in field 'bigIntegerValue')");
184184

185185
pojo = new InvalidPOJO();
186186
pojo.byteValue = 0;
187187
expectedErrorMessages.put(
188188
pojo,
189-
"Could not serialize object. Numbers of type Byte are not supported, please use an int, long, float or double (found in field 'byteValue')");
189+
"Could not serialize object. Numbers of type Byte are not supported, please use an int, long, float, double or BigDecimal (found in field 'byteValue')");
190190

191191
pojo = new InvalidPOJO();
192192
pojo.shortValue = 0;
193193
expectedErrorMessages.put(
194194
pojo,
195-
"Could not serialize object. Numbers of type Short are not supported, please use an int, long, float or double (found in field 'shortValue')");
195+
"Could not serialize object. Numbers of type Short are not supported, please use an int, long, float, double or BigDecimal (found in field 'shortValue')");
196196

197197
for (Map.Entry<InvalidPOJO, String> testCase : expectedErrorMessages.entrySet()) {
198198
try {

google-cloud-firestore/src/test/java/com/google/cloud/firestore/MapperTest.java

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.google.common.collect.ImmutableList;
3232
import com.google.firestore.v1.DatabaseRootName;
3333
import java.io.Serializable;
34+
import java.math.BigDecimal;
3435
import java.util.ArrayList;
3536
import java.util.Arrays;
3637
import java.util.Collection;
@@ -39,6 +40,7 @@
3940
import java.util.HashMap;
4041
import java.util.List;
4142
import java.util.Map;
43+
import java.util.Objects;
4244
import java.util.Set;
4345
import org.junit.Test;
4446
import org.junit.runner.RunWith;
@@ -74,6 +76,31 @@ public double getValue() {
7476
}
7577
}
7678

79+
private static class BigDecimalBean {
80+
private BigDecimal value;
81+
82+
public BigDecimal getValue() {
83+
return value;
84+
}
85+
86+
@Override
87+
public boolean equals(Object o) {
88+
if (this == o) {
89+
return true;
90+
}
91+
if (o == null || getClass() != o.getClass()) {
92+
return false;
93+
}
94+
BigDecimalBean bean = (BigDecimalBean) o;
95+
return Objects.equals(value, bean.value);
96+
}
97+
98+
@Override
99+
public int hashCode() {
100+
return Objects.hash(value);
101+
}
102+
}
103+
77104
private static class FloatBean {
78105
private float value;
79106

@@ -1049,6 +1076,41 @@ public void primitiveDeserializeDouble() {
10491076
}
10501077
}
10511078

1079+
@Test
1080+
public void primitiveDeserializeBigDecimal() {
1081+
BigDecimalBean beanBigdecimal = deserialize("{'value': 123}", BigDecimalBean.class);
1082+
assertEquals(BigDecimal.valueOf(123), beanBigdecimal.value);
1083+
1084+
beanBigdecimal = deserialize("{'value': '123'}", BigDecimalBean.class);
1085+
assertEquals(BigDecimal.valueOf(123), beanBigdecimal.value);
1086+
1087+
// Int
1088+
BigDecimalBean beanInt = deserialize("{'value': 1}", BigDecimalBean.class);
1089+
assertEquals(BigDecimal.valueOf(1), beanInt.value);
1090+
1091+
// Long
1092+
BigDecimalBean beanLong = deserialize("{'value': 1234567890123}", BigDecimalBean.class);
1093+
assertEquals(BigDecimal.valueOf(1234567890123L), beanLong.value);
1094+
1095+
// Double
1096+
BigDecimalBean beanDouble = deserialize("{'value': 1.1}", BigDecimalBean.class);
1097+
assertEquals(BigDecimal.valueOf(1.1), beanDouble.value);
1098+
1099+
// Boolean
1100+
try {
1101+
deserialize("{'value': true}", BigDecimalBean.class);
1102+
fail("Should throw");
1103+
} catch (RuntimeException e) { // ignore
1104+
}
1105+
1106+
// String
1107+
try {
1108+
deserialize("{'value': 'foo'}", BigDecimalBean.class);
1109+
fail("Should throw");
1110+
} catch (RuntimeException e) { // ignore
1111+
}
1112+
}
1113+
10521114
@Test
10531115
public void primitiveDeserializeFloat() {
10541116
FloatBean beanFloat = deserialize("{'value': 1.1}", FloatBean.class);
@@ -1513,6 +1575,23 @@ public void serializeLongBean() {
15131575
assertJson("{'value': 1234567890123}", serialize(bean));
15141576
}
15151577

1578+
@Test
1579+
public void serializeBigDecimalBean() {
1580+
BigDecimalBean bean = new BigDecimalBean();
1581+
bean.value = BigDecimal.valueOf(1.1);
1582+
assertEquals(mapAnyType("value", "1.1"), serialize(bean));
1583+
}
1584+
1585+
@Test
1586+
public void bigDecimalRoundTrip() {
1587+
BigDecimal doubleMaxPlusOne = BigDecimal.valueOf(Double.MAX_VALUE).add(BigDecimal.ONE);
1588+
BigDecimalBean a = new BigDecimalBean();
1589+
a.value = doubleMaxPlusOne;
1590+
Map<String, Object> serialized = (Map<String, Object>) serialize(a);
1591+
BigDecimalBean b = convertToCustomClass(serialized, BigDecimalBean.class);
1592+
assertEquals(a, b);
1593+
}
1594+
15161595
@Test
15171596
public void serializeBooleanBean() {
15181597
BooleanBean bean = new BooleanBean();
@@ -1827,7 +1906,7 @@ public void shortsCantBeSerialized() {
18271906
final ShortBean bean = new ShortBean();
18281907
bean.value = 1;
18291908
assertExceptionContains(
1830-
"Numbers of type Short are not supported, please use an int, long, float or double (found in field 'value')",
1909+
"Numbers of type Short are not supported, please use an int, long, float, double or BigDecimal (found in field 'value')",
18311910
new Runnable() {
18321911
@Override
18331912
public void run() {
@@ -1841,7 +1920,7 @@ public void bytesCantBeSerialized() {
18411920
final ByteBean bean = new ByteBean();
18421921
bean.value = 1;
18431922
assertExceptionContains(
1844-
"Numbers of type Byte are not supported, please use an int, long, float or double (found in field 'value')",
1923+
"Numbers of type Byte are not supported, please use an int, long, float, double or BigDecimal (found in field 'value')",
18451924
new Runnable() {
18461925
@Override
18471926
public void run() {
@@ -2478,7 +2557,7 @@ public void serializationFailureIncludesPath() {
24782557
} catch (RuntimeException e) {
24792558
assertEquals(
24802559
"Could not serialize object. Numbers of type Short are not supported, please use an int, "
2481-
+ "long, float or double (found in field 'value.inner.value.short')",
2560+
+ "long, float, double or BigDecimal (found in field 'value.inner.value.short')",
24822561
e.getMessage());
24832562
}
24842563
}

0 commit comments

Comments
 (0)