Skip to content

Commit 4be6dbb

Browse files
authored
Add encoding support multiple types (#1005)
* Add encoding support for: * primitive longs * Enums with ability to register custom encoders if desired * All java.lang.Number subclasses Note that longs and all Numbers are represented as numeric values, not strings. While this could be a problem when parsed with a JavaScript parser, it is not an issue for us since JS is not involved on the client or the server. * Address review comments.
1 parent 4492da7 commit 4be6dbb

File tree

6 files changed

+123
-32
lines changed

6 files changed

+123
-32
lines changed

encoders/firebase-encoders/api.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.google.firebase.encoders {
1818
method @NonNull public com.google.firebase.encoders.ObjectEncoderContext add(@NonNull String, @Nullable Object) throws com.google.firebase.encoders.EncodingException;
1919
method @NonNull public com.google.firebase.encoders.ObjectEncoderContext add(@NonNull String, double) throws com.google.firebase.encoders.EncodingException;
2020
method @NonNull public com.google.firebase.encoders.ObjectEncoderContext add(@NonNull String, int) throws com.google.firebase.encoders.EncodingException;
21+
method @NonNull public com.google.firebase.encoders.ObjectEncoderContext add(@NonNull String, long) throws com.google.firebase.encoders.EncodingException;
2122
method @NonNull public com.google.firebase.encoders.ObjectEncoderContext add(@NonNull String, boolean) throws com.google.firebase.encoders.EncodingException;
2223
}
2324

@@ -28,6 +29,7 @@ package com.google.firebase.encoders {
2829
method @NonNull public com.google.firebase.encoders.ValueEncoderContext add(@Nullable String) throws com.google.firebase.encoders.EncodingException;
2930
method @NonNull public com.google.firebase.encoders.ValueEncoderContext add(double) throws com.google.firebase.encoders.EncodingException;
3031
method @NonNull public com.google.firebase.encoders.ValueEncoderContext add(int) throws com.google.firebase.encoders.EncodingException;
32+
method @NonNull public com.google.firebase.encoders.ValueEncoderContext add(long) throws com.google.firebase.encoders.EncodingException;
3133
method @NonNull public com.google.firebase.encoders.ValueEncoderContext add(boolean) throws com.google.firebase.encoders.EncodingException;
3234
method @NonNull public com.google.firebase.encoders.ValueEncoderContext add(@NonNull byte[]) throws com.google.firebase.encoders.EncodingException;
3335
}

encoders/firebase-encoders/src/main/java/com/google/firebase/encoders/ObjectEncoderContext.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ ObjectEncoderContext add(@NonNull String name, @Nullable Object obj)
5656
ObjectEncoderContext add(@NonNull String name, double value)
5757
throws IOException, EncodingException;
5858

59-
// TODO: Add support for `long`.
6059
/** Add an entry with {@code name} mapped to the encoded primitive type of {@code value}. */
6160
@NonNull
6261
ObjectEncoderContext add(@NonNull String name, int value) throws IOException, EncodingException;
6362

63+
/** Add an entry with {@code name} mapped to the encoded primitive type of {@code value}. */
64+
@NonNull
65+
ObjectEncoderContext add(@NonNull String name, long value) throws IOException, EncodingException;
66+
6467
/** Add an entry with {@code name} mapped to the encoded primitive type of {@code value}. */
6568
@NonNull
6669
ObjectEncoderContext add(@NonNull String name, boolean value)

encoders/firebase-encoders/src/main/java/com/google/firebase/encoders/ValueEncoderContext.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@ public interface ValueEncoderContext {
3434
@NonNull
3535
ValueEncoderContext add(double value) throws IOException, EncodingException;
3636

37-
// TODO: Add support for `long`.
3837
/** Adds {@code value} as a primitive encoded value. */
3938
@NonNull
4039
ValueEncoderContext add(int value) throws IOException, EncodingException;
4140

41+
/** Adds {@code value} as a primitive encoded value. */
42+
@NonNull
43+
ValueEncoderContext add(long value) throws IOException, EncodingException;
44+
4245
/** Adds {@code value} as a primitive encoded value. */
4346
@NonNull
4447
ValueEncoderContext add(boolean value) throws IOException, EncodingException;

encoders/firebase-encoders/src/main/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,11 @@ public void encode(@Nullable Date o, @NonNull ValueEncoderContext ctx)
5353
}
5454

5555
private static final ValueEncoder<String> STRING_ENCODER = (o, ctx) -> ctx.add(o);
56-
private static final ValueEncoder<Integer> INTEGER_ENCODER = (o, ctx) -> ctx.add(o);
57-
private static final ValueEncoder<Double> DOUBLE_ENCODER = (o, ctx) -> ctx.add(o);
5856
private static final ValueEncoder<Boolean> BOOLEAN_ENCODER = (o, ctx) -> ctx.add(o);
5957
private static final TimestampEncoder TIMESTAMP_ENCODER = new TimestampEncoder();
6058

6159
public JsonDataEncoderBuilder() {
6260
registerEncoder(String.class, STRING_ENCODER);
63-
registerEncoder(Integer.class, INTEGER_ENCODER);
64-
registerEncoder(Double.class, DOUBLE_ENCODER);
6561
registerEncoder(Boolean.class, BOOLEAN_ENCODER);
6662
registerEncoder(Date.class, TIMESTAMP_ENCODER);
6763
}

encoders/firebase-encoders/src/main/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ public JsonValueObjectEncoderContext add(@NonNull String name, int value)
7171
return add(value);
7272
}
7373

74+
@NonNull
75+
@Override
76+
public JsonValueObjectEncoderContext add(@NonNull String name, long value)
77+
throws IOException, EncodingException {
78+
jsonWriter.name(name);
79+
return add(value);
80+
}
81+
7482
@NonNull
7583
@Override
7684
public JsonValueObjectEncoderContext add(@NonNull String name, boolean value)
@@ -101,6 +109,13 @@ public JsonValueObjectEncoderContext add(int value) throws IOException, Encoding
101109
return this;
102110
}
103111

112+
@NonNull
113+
@Override
114+
public JsonValueObjectEncoderContext add(long value) throws IOException, EncodingException {
115+
jsonWriter.value(value);
116+
return this;
117+
}
118+
104119
@NonNull
105120
@Override
106121
public JsonValueObjectEncoderContext add(boolean value) throws IOException, EncodingException {
@@ -126,34 +141,42 @@ JsonValueObjectEncoderContext add(@Nullable Object o) throws IOException, Encodi
126141
jsonWriter.nullValue();
127142
return this;
128143
}
129-
// TODO: Add missing primitive types.
144+
if (o instanceof Number) {
145+
jsonWriter.value((Number) o);
146+
return this;
147+
}
148+
130149
if (o.getClass().isArray()) {
131150
// Byte[] are a special case of arrays, because they are not mapped to an array, but to a
132151
// string.
133-
if (o.getClass().getComponentType() == byte.class) {
134-
byte[] bytes = (byte[]) o;
135-
return add(bytes);
152+
if (o instanceof byte[]) {
153+
return add((byte[]) o);
136154
}
137155

138156
jsonWriter.beginArray();
139-
if (o.getClass().getComponentType() == int.class) {
140-
int[] array = (int[]) o;
141-
for (int item : array) {
157+
if (o instanceof int[]) {
158+
for (int item : (int[]) o) {
142159
jsonWriter.value(item);
143160
}
144-
} else if (o.getClass().getComponentType() == double.class) {
145-
double[] array = (double[]) o;
146-
for (double item : array) {
161+
} else if (o instanceof long[]) {
162+
for (long item : (long[]) o) {
163+
add(item);
164+
}
165+
} else if (o instanceof double[]) {
166+
for (double item : (double[]) o) {
147167
jsonWriter.value(item);
148168
}
149-
} else if (o.getClass().getComponentType() == boolean.class) {
150-
boolean[] array = (boolean[]) o;
151-
for (boolean item : array) {
169+
} else if (o instanceof boolean[]) {
170+
for (boolean item : (boolean[]) o) {
152171
jsonWriter.value(item);
153172
}
173+
} else if (o instanceof Number[]) {
174+
for (Number item : (Number[]) o) {
175+
add(item);
176+
}
177+
154178
} else {
155-
Object[] array = (Object[]) o;
156-
for (Object item : array) {
179+
for (Object item : (Object[]) o) {
157180
add(item);
158181
}
159182
}
@@ -184,6 +207,12 @@ JsonValueObjectEncoderContext add(@Nullable Object o) throws IOException, Encodi
184207
return this;
185208
}
186209

210+
// Process enum last if it does not have a custom encoder registered.
211+
if (o instanceof Enum) {
212+
add(((Enum) o).name());
213+
return this;
214+
}
215+
187216
throw new EncodingException(
188217
"Couldn't find encoder for type " + o.getClass().getCanonicalName());
189218
}

encoders/firebase-encoders/src/test/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContextTest.java

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ public void testEncodingPrimitiveTypes() throws IOException, EncodingException {
6060
ObjectEncoder<DummyClass> objectEncoder =
6161
(o, ctx) -> {
6262
ctx.add("String", "string")
63-
.add("Integer", 2)
64-
.add("Double", 2.2d)
65-
.add("Boolean", false)
66-
.add("Null", null);
63+
.add("int", 2)
64+
.add("long", 42L)
65+
.add("double", 2.2d)
66+
.add("boolean", false)
67+
.add("null", null);
6768
};
6869

6970
String result =
@@ -74,7 +75,7 @@ public void testEncodingPrimitiveTypes() throws IOException, EncodingException {
7475

7576
assertThat(result)
7677
.isEqualTo(
77-
"{\"String\":\"string\",\"Integer\":2,\"Double\":2.2,\"Boolean\":false,\"Null\":null}");
78+
"{\"String\":\"string\",\"int\":2,\"long\":42,\"double\":2.2,\"boolean\":false,\"null\":null}");
7879
}
7980

8081
@Test
@@ -98,10 +99,11 @@ public void testEncodingArrayPrimitives() throws IOException, EncodingException
9899
ObjectEncoder<DummyClass> objectEncoder =
99100
(o, ctx) -> {
100101
ctx.add("String", new String[] {"string1", "string2"})
101-
.add("Integer", new int[] {1, 2})
102-
.add("Double", new double[] {1.1d, 2.2d})
103-
.add("Boolean", new boolean[] {true, false})
104-
.add("Null", new String[] {null, null});
102+
.add("int", new int[] {1, 2})
103+
.add("long", new long[] {3L, 4L})
104+
.add("double", new double[] {1.1d, 2.2d})
105+
.add("boolean", new boolean[] {true, false})
106+
.add("null", new String[] {null, null});
105107
};
106108

107109
String result =
@@ -113,8 +115,64 @@ public void testEncodingArrayPrimitives() throws IOException, EncodingException
113115
assertThat(result)
114116
.isEqualTo(
115117
String.format(
116-
"{\"String\":%s,\"Integer\":%s,\"Double\":%s,\"Boolean\":%s,\"Null\":%s}",
117-
"[\"string1\",\"string2\"]", "[1,2]", "[1.1,2.2]", "[true,false]", "[null,null]"));
118+
"{\"String\":%s,\"int\":%s,\"long\":%s,\"double\":%s,\"boolean\":%s,\"null\":%s}",
119+
"[\"string1\",\"string2\"]",
120+
"[1,2]",
121+
"[3,4]",
122+
"[1.1,2.2]",
123+
"[true,false]",
124+
"[null,null]"));
125+
}
126+
127+
@Test
128+
public void testEncodingNumbers() throws EncodingException {
129+
ObjectEncoder<DummyClass> objectEncoder =
130+
(o, ctx) -> ctx.add("Number", new Number[] {1, 2473946328429347632L, 0.0d});
131+
132+
String result =
133+
new JsonDataEncoderBuilder()
134+
.registerEncoder(DummyClass.class, objectEncoder)
135+
.build()
136+
.encode(DummyClass.INSTANCE);
137+
138+
assertThat(result).isEqualTo(String.format("{\"Number\":%s}", "[1,2473946328429347632,0.0]"));
139+
}
140+
141+
@Test
142+
public void testEncodingLongs() throws EncodingException {
143+
ObjectEncoder<DummyClass> objectEncoder =
144+
(o, ctx) -> ctx.add("long", new long[] {1L, 2473946328429347632L});
145+
146+
String result =
147+
new JsonDataEncoderBuilder()
148+
.registerEncoder(DummyClass.class, objectEncoder)
149+
.build()
150+
.encode(DummyClass.INSTANCE);
151+
152+
assertThat(result).isEqualTo(String.format("{\"long\":%s}", "[1,2473946328429347632]"));
153+
}
154+
155+
enum MyEnum {
156+
VALUE_1,
157+
VALUE_2
158+
}
159+
160+
@Test
161+
public void testEncodingEnum_withNoCustomEncoder() throws EncodingException {
162+
String result =
163+
new JsonDataEncoderBuilder().build().encode(new MyEnum[] {MyEnum.VALUE_1, MyEnum.VALUE_2});
164+
assertThat(result).isEqualTo("[\"VALUE_1\",\"VALUE_2\"]");
165+
}
166+
167+
@Test
168+
public void testEncodingEnum_withCustomEncoder() throws EncodingException {
169+
ValueEncoder<MyEnum> encoder = (o, ctx) -> ctx.add(o.name().toLowerCase());
170+
String result =
171+
new JsonDataEncoderBuilder()
172+
.registerEncoder(MyEnum.class, encoder)
173+
.build()
174+
.encode(new MyEnum[] {MyEnum.VALUE_1, MyEnum.VALUE_2});
175+
assertThat(result).isEqualTo("[\"value_1\",\"value_2\"]");
118176
}
119177

120178
@Test

0 commit comments

Comments
 (0)