From c21ccaec437242756e4199b887037db614b67d31 Mon Sep 17 00:00:00 2001 From: Matt Willis Date: Wed, 29 Jan 2020 15:10:40 -0500 Subject: [PATCH 1/5] Add support for ignoring nulls in JsonDataEncoder --- .../encoders/json/JsonDataEncoderBuilder.java | 11 ++++- .../json/JsonValueObjectEncoderContext.java | 40 +++++++++++++++---- .../json/JsonDataEncoderBuilderTests.java | 40 +++++++++++++++++++ 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java index 847660431a4..3448444bb3b 100644 --- a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java +++ b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java @@ -35,7 +35,7 @@ public final class JsonDataEncoderBuilder implements EncoderConfig { - private final ObjectEncoder DEFAULT_FALLBACK_ENCODER = + private static final ObjectEncoder DEFAULT_FALLBACK_ENCODER = (o, ctx) -> { throw new EncodingException( "Couldn't find encoder for type " + o.getClass().getCanonicalName()); @@ -44,6 +44,7 @@ public final class JsonDataEncoderBuilder implements EncoderConfig, ObjectEncoder> objectEncoders = new HashMap<>(); private final Map, ValueEncoder> valueEncoders = new HashMap<>(); private ObjectEncoder fallbackEncoder = DEFAULT_FALLBACK_ENCODER; + private boolean ignoreNulls = false; private static final class TimestampEncoder implements ValueEncoder { private static final DateFormat rfc339; @@ -104,6 +105,12 @@ public JsonDataEncoderBuilder configureWith(@NonNull Configurator config) { return this; } + @NonNull + public JsonDataEncoderBuilder ignoreNulls(boolean ignore) { + this.ignoreNulls = ignore; + return this; + } + @NonNull public DataEncoder build() { return new DataEncoder() { @@ -112,7 +119,7 @@ public void encode(@NonNull Object o, @NonNull Writer writer) throws IOException, EncodingException { JsonValueObjectEncoderContext encoderContext = new JsonValueObjectEncoderContext( - writer, objectEncoders, valueEncoders, fallbackEncoder); + writer, objectEncoders, valueEncoders, fallbackEncoder, ignoreNulls); encoderContext.add(o, false); encoderContext.close(); } diff --git a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java index 2a8a94e1cfc..2bdc38f4eb5 100644 --- a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java +++ b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java @@ -31,22 +31,30 @@ final class JsonValueObjectEncoderContext implements ObjectEncoderContext, ValueEncoderContext { + private interface AddMethod { + JsonValueObjectEncoderContext invoke(@NonNull String name, @Nullable Object o) + throws IOException, EncodingException; + } + private JsonValueObjectEncoderContext childContext = null; private boolean active = true; private final JsonWriter jsonWriter; private final Map, ObjectEncoder> objectEncoders; private final Map, ValueEncoder> valueEncoders; private final ObjectEncoder fallbackEncoder; + private final AddMethod addMethod; JsonValueObjectEncoderContext( @NonNull Writer writer, @NonNull Map, ObjectEncoder> objectEncoders, @NonNull Map, ValueEncoder> valueEncoders, - ObjectEncoder fallbackEncoder) { + ObjectEncoder fallbackEncoder, + boolean ignoreNulls) { this.jsonWriter = new JsonWriter(writer); this.objectEncoders = objectEncoders; this.valueEncoders = valueEncoders; this.fallbackEncoder = fallbackEncoder; + this.addMethod = ignoreNulls ? this::internalAddIgnoreNulls : this::internalAdd; } private JsonValueObjectEncoderContext(JsonValueObjectEncoderContext anotherContext) { @@ -54,19 +62,14 @@ private JsonValueObjectEncoderContext(JsonValueObjectEncoderContext anotherConte this.objectEncoders = anotherContext.objectEncoders; this.valueEncoders = anotherContext.valueEncoders; this.fallbackEncoder = anotherContext.fallbackEncoder; + this.addMethod = anotherContext.addMethod; } @NonNull @Override public JsonValueObjectEncoderContext add(@NonNull String name, @Nullable Object o) throws IOException, EncodingException { - maybeUnNest(); - jsonWriter.name(name); - if (o == null) { - jsonWriter.nullValue(); - return this; - } - return add(o, false); + return addMethod.invoke(name, o); } @NonNull @@ -311,4 +314,25 @@ private void maybeUnNest() throws IOException { jsonWriter.endObject(); } } + + private JsonValueObjectEncoderContext internalAdd(@NonNull String name, @Nullable Object o) + throws IOException, EncodingException { + maybeUnNest(); + jsonWriter.name(name); + if (o == null) { + jsonWriter.nullValue(); + return this; + } + return add(o, false); + } + + private JsonValueObjectEncoderContext internalAddIgnoreNulls( + @NonNull String name, @Nullable Object o) throws IOException, EncodingException { + if (o == null) { + return this; + } + maybeUnNest(); + jsonWriter.name(name); + return add(o, false); + } } diff --git a/encoders/firebase-encoders-json/src/test/java/com/google/firebase/encoders/json/JsonDataEncoderBuilderTests.java b/encoders/firebase-encoders-json/src/test/java/com/google/firebase/encoders/json/JsonDataEncoderBuilderTests.java index fe7275f7ade..bd1da41d2c9 100644 --- a/encoders/firebase-encoders-json/src/test/java/com/google/firebase/encoders/json/JsonDataEncoderBuilderTests.java +++ b/encoders/firebase-encoders-json/src/test/java/com/google/firebase/encoders/json/JsonDataEncoderBuilderTests.java @@ -22,6 +22,7 @@ import com.google.firebase.encoders.ObjectEncoderContext; import com.google.firebase.encoders.ValueEncoderContext; import java.util.Collections; +import java.util.HashMap; import org.junit.Test; import org.junit.runner.RunWith; @@ -61,4 +62,43 @@ public void configureWith_shouldCorrectlyRegisterValueEncoder() throws EncodingE assertThat(encoder.encode(Collections.singletonMap("foo", new Foo()))) .isEqualTo("{\"foo\":\"value\"}"); } + + @Test + public void ignoreNulls_shouldCorrectlyEncodeObjectIgnoringNullObjects() + throws EncodingException { + DataEncoder encoder = + new JsonDataEncoderBuilder() + .configureWith( + cfg -> + cfg.registerEncoder( + Foo.class, + (Foo s, ObjectEncoderContext ctx) -> { + ctx.add("foo", "value"); + ctx.add("bar", null); + })) + .ignoreNulls(true) + .build(); + + assertThat(encoder.encode(new Foo())).isEqualTo("{\"foo\":\"value\"}"); + } + + @Test + public void ignoreNulls_shouldCorrectlyEncodeValueIgnoringNullObjects() throws EncodingException { + DataEncoder encoder = + new JsonDataEncoderBuilder() + .configureWith( + cfg -> + cfg.registerEncoder( + Foo.class, + (Foo s, ValueEncoderContext ctx) -> { + ctx.add("value"); + })) + .ignoreNulls(true) + .build(); + + HashMap fooMap = new HashMap<>(); + fooMap.put("foo", new Foo()); + fooMap.put("bar", null); + assertThat(encoder.encode(fooMap)).isEqualTo("{\"foo\":\"value\"}"); + } } From 94604dc579327ff46ca459d8243fd13879b4e331 Mon Sep 17 00:00:00 2001 From: Matt Willis Date: Wed, 29 Jan 2020 15:48:50 -0500 Subject: [PATCH 2/5] Change 'ignoreNulls' to 'ignoreNullValues' --- .../firebase/encoders/json/JsonDataEncoderBuilder.java | 8 ++++---- .../encoders/json/JsonValueObjectEncoderContext.java | 6 +++--- .../encoders/json/JsonDataEncoderBuilderTests.java | 9 +++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java index 3448444bb3b..0ea6307130c 100644 --- a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java +++ b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonDataEncoderBuilder.java @@ -44,7 +44,7 @@ public final class JsonDataEncoderBuilder implements EncoderConfig, ObjectEncoder> objectEncoders = new HashMap<>(); private final Map, ValueEncoder> valueEncoders = new HashMap<>(); private ObjectEncoder fallbackEncoder = DEFAULT_FALLBACK_ENCODER; - private boolean ignoreNulls = false; + private boolean ignoreNullValues = false; private static final class TimestampEncoder implements ValueEncoder { private static final DateFormat rfc339; @@ -106,8 +106,8 @@ public JsonDataEncoderBuilder configureWith(@NonNull Configurator config) { } @NonNull - public JsonDataEncoderBuilder ignoreNulls(boolean ignore) { - this.ignoreNulls = ignore; + public JsonDataEncoderBuilder ignoreNullValues(boolean ignore) { + this.ignoreNullValues = ignore; return this; } @@ -119,7 +119,7 @@ public void encode(@NonNull Object o, @NonNull Writer writer) throws IOException, EncodingException { JsonValueObjectEncoderContext encoderContext = new JsonValueObjectEncoderContext( - writer, objectEncoders, valueEncoders, fallbackEncoder, ignoreNulls); + writer, objectEncoders, valueEncoders, fallbackEncoder, ignoreNullValues); encoderContext.add(o, false); encoderContext.close(); } diff --git a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java index 2bdc38f4eb5..a0cbd3841c4 100644 --- a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java +++ b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java @@ -49,12 +49,12 @@ JsonValueObjectEncoderContext invoke(@NonNull String name, @Nullable Object o) @NonNull Map, ObjectEncoder> objectEncoders, @NonNull Map, ValueEncoder> valueEncoders, ObjectEncoder fallbackEncoder, - boolean ignoreNulls) { + boolean ignoreNullValues) { this.jsonWriter = new JsonWriter(writer); this.objectEncoders = objectEncoders; this.valueEncoders = valueEncoders; this.fallbackEncoder = fallbackEncoder; - this.addMethod = ignoreNulls ? this::internalAddIgnoreNulls : this::internalAdd; + this.addMethod = ignoreNullValues ? this::internalAddIgnoreNullValues : this::internalAdd; } private JsonValueObjectEncoderContext(JsonValueObjectEncoderContext anotherContext) { @@ -326,7 +326,7 @@ private JsonValueObjectEncoderContext internalAdd(@NonNull String name, @Nullabl return add(o, false); } - private JsonValueObjectEncoderContext internalAddIgnoreNulls( + private JsonValueObjectEncoderContext internalAddIgnoreNullValues( @NonNull String name, @Nullable Object o) throws IOException, EncodingException { if (o == null) { return this; diff --git a/encoders/firebase-encoders-json/src/test/java/com/google/firebase/encoders/json/JsonDataEncoderBuilderTests.java b/encoders/firebase-encoders-json/src/test/java/com/google/firebase/encoders/json/JsonDataEncoderBuilderTests.java index bd1da41d2c9..4f2ded9e70f 100644 --- a/encoders/firebase-encoders-json/src/test/java/com/google/firebase/encoders/json/JsonDataEncoderBuilderTests.java +++ b/encoders/firebase-encoders-json/src/test/java/com/google/firebase/encoders/json/JsonDataEncoderBuilderTests.java @@ -64,7 +64,7 @@ public void configureWith_shouldCorrectlyRegisterValueEncoder() throws EncodingE } @Test - public void ignoreNulls_shouldCorrectlyEncodeObjectIgnoringNullObjects() + public void ignoreNullValues_shouldCorrectlyEncodeObjectIgnoringNullObjects() throws EncodingException { DataEncoder encoder = new JsonDataEncoderBuilder() @@ -76,14 +76,15 @@ public void ignoreNulls_shouldCorrectlyEncodeObjectIgnoringNullObjects() ctx.add("foo", "value"); ctx.add("bar", null); })) - .ignoreNulls(true) + .ignoreNullValues(true) .build(); assertThat(encoder.encode(new Foo())).isEqualTo("{\"foo\":\"value\"}"); } @Test - public void ignoreNulls_shouldCorrectlyEncodeValueIgnoringNullObjects() throws EncodingException { + public void ignoreNullValues_shouldCorrectlyEncodeValueIgnoringNullObjects() + throws EncodingException { DataEncoder encoder = new JsonDataEncoderBuilder() .configureWith( @@ -93,7 +94,7 @@ public void ignoreNulls_shouldCorrectlyEncodeValueIgnoringNullObjects() throws E (Foo s, ValueEncoderContext ctx) -> { ctx.add("value"); })) - .ignoreNulls(true) + .ignoreNullValues(true) .build(); HashMap fooMap = new HashMap<>(); From 5113d03c0990076b196e3172bad3720e1e968d3d Mon Sep 17 00:00:00 2001 From: Matt Willis Date: Wed, 29 Jan 2020 15:51:29 -0500 Subject: [PATCH 3/5] Add to API file and bump version --- encoders/firebase-encoders-json/api.txt | 1 + encoders/firebase-encoders-json/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/encoders/firebase-encoders-json/api.txt b/encoders/firebase-encoders-json/api.txt index 947497afe02..ebf7a8685f0 100644 --- a/encoders/firebase-encoders-json/api.txt +++ b/encoders/firebase-encoders-json/api.txt @@ -72,6 +72,7 @@ package com.google.firebase.encoders.json { ctor public JsonDataEncoderBuilder(); method @NonNull public com.google.firebase.encoders.DataEncoder build(); method @NonNull public com.google.firebase.encoders.json.JsonDataEncoderBuilder configureWith(@NonNull com.google.firebase.encoders.config.Configurator); + method @NonNull public com.google.firebase.encoders.json.JsonDataEncoderBuilder ignoreNullValues(boolean); method @NonNull public com.google.firebase.encoders.json.JsonDataEncoderBuilder registerEncoder(@NonNull Class, @NonNull com.google.firebase.encoders.ObjectEncoder); method @NonNull public com.google.firebase.encoders.json.JsonDataEncoderBuilder registerEncoder(@NonNull Class, @NonNull com.google.firebase.encoders.ValueEncoder); method @NonNull public com.google.firebase.encoders.json.JsonDataEncoderBuilder registerFallbackEncoder(@NonNull com.google.firebase.encoders.ObjectEncoder); diff --git a/encoders/firebase-encoders-json/gradle.properties b/encoders/firebase-encoders-json/gradle.properties index 1c68d126d26..94dcffed351 100644 --- a/encoders/firebase-encoders-json/gradle.properties +++ b/encoders/firebase-encoders-json/gradle.properties @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -version=16.0.1 +version=16.1.0 From fd90d1a94ca5ae6e6167e395cc2a93df34d41f31 Mon Sep 17 00:00:00 2001 From: Matt Willis Date: Wed, 29 Jan 2020 16:47:11 -0500 Subject: [PATCH 4/5] Replace dynamic dispatch with simpler 'if' check --- .../encoders/json/JsonValueObjectEncoderContext.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java index a0cbd3841c4..3514c0157a8 100644 --- a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java +++ b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java @@ -42,7 +42,7 @@ JsonValueObjectEncoderContext invoke(@NonNull String name, @Nullable Object o) private final Map, ObjectEncoder> objectEncoders; private final Map, ValueEncoder> valueEncoders; private final ObjectEncoder fallbackEncoder; - private final AddMethod addMethod; + private final boolean ignoreNullValues; JsonValueObjectEncoderContext( @NonNull Writer writer, @@ -54,7 +54,7 @@ JsonValueObjectEncoderContext invoke(@NonNull String name, @Nullable Object o) this.objectEncoders = objectEncoders; this.valueEncoders = valueEncoders; this.fallbackEncoder = fallbackEncoder; - this.addMethod = ignoreNullValues ? this::internalAddIgnoreNullValues : this::internalAdd; + this.ignoreNullValues = ignoreNullValues; } private JsonValueObjectEncoderContext(JsonValueObjectEncoderContext anotherContext) { @@ -62,14 +62,17 @@ private JsonValueObjectEncoderContext(JsonValueObjectEncoderContext anotherConte this.objectEncoders = anotherContext.objectEncoders; this.valueEncoders = anotherContext.valueEncoders; this.fallbackEncoder = anotherContext.fallbackEncoder; - this.addMethod = anotherContext.addMethod; + this.ignoreNullValues = anotherContext.ignoreNullValues; } @NonNull @Override public JsonValueObjectEncoderContext add(@NonNull String name, @Nullable Object o) throws IOException, EncodingException { - return addMethod.invoke(name, o); + if (ignoreNullValues) { + return internalAddIgnoreNullValues(name, o); + } + return internalAdd(name, o); } @NonNull From 202f3f9f95aca0abd27bf662911339a0f33b6e4c Mon Sep 17 00:00:00 2001 From: Matt Willis Date: Wed, 29 Jan 2020 16:48:26 -0500 Subject: [PATCH 5/5] Remove interface --- .../encoders/json/JsonValueObjectEncoderContext.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java index 3514c0157a8..9f37311d4ba 100644 --- a/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java +++ b/encoders/firebase-encoders-json/src/json/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java @@ -31,11 +31,6 @@ final class JsonValueObjectEncoderContext implements ObjectEncoderContext, ValueEncoderContext { - private interface AddMethod { - JsonValueObjectEncoderContext invoke(@NonNull String name, @Nullable Object o) - throws IOException, EncodingException; - } - private JsonValueObjectEncoderContext childContext = null; private boolean active = true; private final JsonWriter jsonWriter;