From 52b899cf2d41d7e50b8bfc186122d8c7dad77e97 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Tue, 25 Oct 2022 13:15:55 +0100 Subject: [PATCH 1/2] Fix deserialization of attributes marked with @SerializedName --- .../META-INF/MANIFEST.MF | 1 + .../json/LowercaseEnumTypeAdapterFactory.java | 31 ++++++++++++++++--- .../targetplatform.target | 14 +++++++-- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/base/uk.ac.stfc.isis.ibex.configserver/META-INF/MANIFEST.MF b/base/uk.ac.stfc.isis.ibex.configserver/META-INF/MANIFEST.MF index 0ff26093f5..6c59481b1a 100644 --- a/base/uk.ac.stfc.isis.ibex.configserver/META-INF/MANIFEST.MF +++ b/base/uk.ac.stfc.isis.ibex.configserver/META-INF/MANIFEST.MF @@ -18,6 +18,7 @@ Require-Bundle: org.eclipse.core.runtime, uk.ac.stfc.isis.ibex.alarm, org.diirt.vtype;bundle-version="3.1.5", com.google.guava, + com.google.gson, uk.ac.stfc.isis.ibex.managermode, uk.ac.stfc.isis.ibex.validators;bundle-version="1.0.0" Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/base/uk.ac.stfc.isis.ibex.epics/src/uk/ac/stfc/isis/ibex/epics/conversion/json/LowercaseEnumTypeAdapterFactory.java b/base/uk.ac.stfc.isis.ibex.epics/src/uk/ac/stfc/isis/ibex/epics/conversion/json/LowercaseEnumTypeAdapterFactory.java index 35dd0a7120..39377d267e 100644 --- a/base/uk.ac.stfc.isis.ibex.epics/src/uk/ac/stfc/isis/ibex/epics/conversion/json/LowercaseEnumTypeAdapterFactory.java +++ b/base/uk.ac.stfc.isis.ibex.epics/src/uk/ac/stfc/isis/ibex/epics/conversion/json/LowercaseEnumTypeAdapterFactory.java @@ -23,10 +23,12 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; +import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -48,9 +50,23 @@ public TypeAdapter create(Gson gson, TypeToken type) { return null; } - final Map lowercaseToConstant = new HashMap(); - for (T constant : rawType.getEnumConstants()) { - lowercaseToConstant.put(toLowercase(constant), constant); + final Map jsonToEnumConstant = new HashMap(); + for (var constant : rawType.getEnumConstants()) { + String name = ((Enum) constant).name(); + + try { + // Try to get an @SerializedName annotation. + // If the SerializedName annotation is present on the enum member, + // always use it in preference to the lowercase rule. + var annotation = rawType.getField(name).getAnnotation(SerializedName.class); + if (annotation != null) { + jsonToEnumConstant.put(annotation.value(), constant); + } else { + jsonToEnumConstant.put(toLowercase(constant), constant); + } + } catch (NoSuchFieldException | SecurityException e) { + throw new RuntimeException(e); + } } return new TypeAdapter() { @@ -60,6 +76,13 @@ public void write(JsonWriter out, T value) throws IOException { out.nullValue(); } else { out.value(toLowercase(value)); + for (var entry : jsonToEnumConstant.entrySet()) { + if (Objects.equals(entry.getValue(), value)) { + out.value(entry.getKey()); + return; + } + } + throw new RuntimeException("Invalid enum constant " + value + " provided to TypeAdapter write(). Allowed values: " + jsonToEnumConstant); } } @@ -69,7 +92,7 @@ public T read(JsonReader reader) throws IOException { reader.nextNull(); return null; } else { - return lowercaseToConstant.get(reader.nextString()); + return jsonToEnumConstant.get(reader.nextString()); } } }; diff --git a/base/uk.ac.stfc.isis.ibex.targetplatform/targetplatform.target b/base/uk.ac.stfc.isis.ibex.targetplatform/targetplatform.target index 94444a5ca6..bdac125638 100644 --- a/base/uk.ac.stfc.isis.ibex.targetplatform/targetplatform.target +++ b/base/uk.ac.stfc.isis.ibex.targetplatform/targetplatform.target @@ -93,8 +93,6 @@ - - @@ -219,7 +217,7 @@ - + org.glassfish.tyrus.bundles @@ -229,6 +227,16 @@ + + + + com.google.code.gson + gson + 2.9.1 + jar + + + linux From 88bb6db04d4013d97cf8d997cf3b1b74cef4c47b Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Tue, 25 Oct 2022 15:04:42 +0100 Subject: [PATCH 2/2] Add unit test --- .../META-INF/MANIFEST.MF | 4 +- .../LowercaseEnumTypeAdapterFactoryTest.java | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 base/uk.ac.stfc.isis.ibex.epics.tests/src/uk/ac/stfc/isis/ibex/epics/tests/conversion/json/LowercaseEnumTypeAdapterFactoryTest.java diff --git a/base/uk.ac.stfc.isis.ibex.epics.tests/META-INF/MANIFEST.MF b/base/uk.ac.stfc.isis.ibex.epics.tests/META-INF/MANIFEST.MF index 4f949c7c03..128918d495 100644 --- a/base/uk.ac.stfc.isis.ibex.epics.tests/META-INF/MANIFEST.MF +++ b/base/uk.ac.stfc.isis.ibex.epics.tests/META-INF/MANIFEST.MF @@ -14,5 +14,7 @@ Require-Bundle: org.junit, org.mockito.mockito-core;bundle-version="4.7.0", net.bytebuddy.byte-buddy;bundle-version="1.12.13", net.bytebuddy.byte-buddy-agent;bundle-version="1.12.13", - org.objenesis;bundle-version="3.2.0" + org.objenesis;bundle-version="3.2.0", + com.google.gson, + com.google.guava Automatic-Module-Name: uk.ac.stfc.isis.ibex.epics.tests diff --git a/base/uk.ac.stfc.isis.ibex.epics.tests/src/uk/ac/stfc/isis/ibex/epics/tests/conversion/json/LowercaseEnumTypeAdapterFactoryTest.java b/base/uk.ac.stfc.isis.ibex.epics.tests/src/uk/ac/stfc/isis/ibex/epics/tests/conversion/json/LowercaseEnumTypeAdapterFactoryTest.java new file mode 100644 index 0000000000..8b5fcb1e01 --- /dev/null +++ b/base/uk.ac.stfc.isis.ibex.epics.tests/src/uk/ac/stfc/isis/ibex/epics/tests/conversion/json/LowercaseEnumTypeAdapterFactoryTest.java @@ -0,0 +1,49 @@ +package uk.ac.stfc.isis.ibex.epics.tests.conversion.json; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.SerializedName; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import uk.ac.stfc.isis.ibex.epics.conversion.ConversionException; +import uk.ac.stfc.isis.ibex.epics.conversion.json.LowercaseEnumTypeAdapterFactory; + +@RunWith(MockitoJUnitRunner.Strict.class) +public class LowercaseEnumTypeAdapterFactoryTest { + + public static enum TestEnum { + IMPLICIT_ONE, + IMPLICIT_TWO; + } + + public static enum TestEnumWithSerializedName { + @SerializedName("name1") EXPLICIT_ONE, + @SerializedName("name2") EXPLICIT_TWO; + } + + public static enum TestEnumMixed { + MIXED_ONE, + @SerializedName("explicit_name") MIXED_TWO; + } + + @Test + public void convert_enum() throws ConversionException { + Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory()) + .create(); + + assertEquals(TestEnum.IMPLICIT_ONE, gson.fromJson("\"implicit_one\"", TestEnum.class)); + assertEquals(TestEnum.IMPLICIT_TWO, gson.fromJson("\"implicit_two\"", TestEnum.class)); + + assertEquals(TestEnumWithSerializedName.EXPLICIT_ONE, gson.fromJson("\"name1\"", TestEnumWithSerializedName.class)); + assertEquals(TestEnumWithSerializedName.EXPLICIT_TWO, gson.fromJson("\"name2\"", TestEnumWithSerializedName.class)); + + assertEquals(TestEnumMixed.MIXED_ONE, gson.fromJson("\"mixed_one\"", TestEnumMixed.class)); + assertEquals(TestEnumMixed.MIXED_TWO, gson.fromJson("\"explicit_name\"", TestEnumMixed.class)); + } +}