From 21c4f6719b61f58ffdf61187780bb53c19255f83 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Wed, 9 Mar 2022 01:11:27 +0300 Subject: [PATCH 1/2] [1.4.0-SNAPSHOT] GsonProperties and GsonConfiguration#excludeFieldsWithModifiers and GsonConfiguration#excludeFieldsWithoutExposeAnnotation properties added --- build.gradle | 2 +- gradle.properties | 2 +- .../gson/configuration/GsonConfiguration.java | 129 +++++++++++++++++- .../gson/configuration/GsonProperties.java | 2 + .../GsonConfigurationFromPropertiesTests.java | 9 ++ src/test/resources/gson.properties | 2 + 6 files changed, 143 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 640cf17..ee4c10b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id "jacoco" - id "maven-publish" id "java-library" + id "maven-publish" id "org.sonarqube" version "3.3" id "com.diffplug.spotless" version "6.1.0" diff --git a/gradle.properties b/gradle.properties index 51e07f0..68ac5d8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=io.goodforgod artifactId=gson-configuration -artifactVersion=1.3.0 +artifactVersion=1.4.0-SNAPSHOT ##### GRADLE ##### diff --git a/src/main/java/io/goodforgod/gson/configuration/GsonConfiguration.java b/src/main/java/io/goodforgod/gson/configuration/GsonConfiguration.java index 824eadd..d5ed5ea 100644 --- a/src/main/java/io/goodforgod/gson/configuration/GsonConfiguration.java +++ b/src/main/java/io/goodforgod/gson/configuration/GsonConfiguration.java @@ -3,11 +3,13 @@ import com.google.gson.FieldNamingPolicy; import com.google.gson.GsonBuilder; import com.google.gson.LongSerializationPolicy; +import java.lang.reflect.Modifier; import java.text.SimpleDateFormat; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatter; import java.time.format.ResolverStyle; -import java.util.Properties; +import java.util.*; +import java.util.stream.Collectors; /** * @author Anton Kurako (GoodforGod) @@ -15,6 +17,42 @@ */ public class GsonConfiguration { + private static final Set DEFAULT_EXCLUDED_MODIFIERS; + + static { + final Set modifiers = new HashSet<>(8); + modifiers.add(FieldModifiers.TRANSIENT); + modifiers.add(FieldModifiers.SYNCHRONIZED); + modifiers.add(FieldModifiers.VOLATILE); + modifiers.add(FieldModifiers.STATIC); + DEFAULT_EXCLUDED_MODIFIERS = Collections.unmodifiableSet(modifiers); + } + + /** + * @see GsonBuilder#excludeFieldsWithModifiers(int...) + */ + public enum FieldModifiers { + + PUBLIC(Modifier.PUBLIC), + PROTECTED(Modifier.PROTECTED), + PRIVATE(Modifier.PRIVATE), + STATIC(Modifier.STATIC), + FINAL(Modifier.FINAL), + SYNCHRONIZED(Modifier.SYNCHRONIZED), + VOLATILE(Modifier.VOLATILE), + TRANSIENT(Modifier.TRANSIENT); + + private final int modifier; + + FieldModifiers(int modifier) { + this.modifier = modifier; + } + + public int modifier() { + return modifier; + } + } + private DateTimeFormatter instantFormat = DateTimeFormatters.ISO_INSTANT; private DateTimeFormatter localDateFormat = DateTimeFormatters.ISO_LOCAL_DATE; private DateTimeFormatter localTimeFormat = DateTimeFormatters.ISO_LOCAL_TIME; @@ -61,6 +99,23 @@ public class GsonConfiguration { */ private boolean serializeNulls = false; + /** + * Configures Gson to exclude all fields from consideration for serialization or deserialization + * that do not have the {@link com.google.gson.annotations.Expose} annotation. + * + * @see GsonBuilder#excludeFieldsWithoutExposeAnnotation() + */ + private boolean excludeFieldsWithoutExposeAnnotation = false; + + /** + * Configures Gson to excludes all class fields that have the specified modifiers. By default, + * Gson will exclude all fields marked transient or static. This method will override that + * behavior. + * + * @see GsonBuilder#excludeFieldsWithModifiers(int...) + */ + private Set excludeFieldsWithModifiers = DEFAULT_EXCLUDED_MODIFIERS; + /** * Enabling this feature will only change the serialized form if the map key is a complex type (i.e. * non-primitive) in its serialized JSON form. @@ -167,6 +222,10 @@ private static GsonConfiguration ofProperties(GsonConfiguration configuration, P final String complexMapKeySerialization = properties.getProperty(GsonProperties.COMPLEX_MAP_KEY_SERIALIZATION); final String serializeSpecialFloatingPointValues = properties .getProperty(GsonProperties.SERIALIZE_SPECIAL_FLOATING_POINT_VALUES); + final String excludeFieldsWithoutExposeAnnotation = properties + .getProperty(GsonProperties.EXCLUDE_FIELDS_WITHOUT_EXPOSE_ANNOTATION); + final String excludeFieldsWithModifiers = properties + .getProperty(GsonProperties.EXCLUDE_FIELDS_WITH_MODIFIERS); if (formatInstant != null) configuration.setInstantFormat(formatInstant); @@ -215,6 +274,15 @@ private static GsonConfiguration ofProperties(GsonConfiguration configuration, P configuration.setComplexMapKeySerialization(Boolean.parseBoolean(complexMapKeySerialization)); if (serializeSpecialFloatingPointValues != null) configuration.setSerializeSpecialFloatingPointValues(Boolean.parseBoolean(serializeSpecialFloatingPointValues)); + if (excludeFieldsWithoutExposeAnnotation != null) + configuration.setExcludeFieldsWithoutExposeAnnotation(Boolean.parseBoolean(excludeFieldsWithoutExposeAnnotation)); + if (excludeFieldsWithModifiers != null) { + final Set modifiers = Arrays.stream(excludeFieldsWithModifiers.split(",")) + .map(FieldModifiers::valueOf) + .collect(Collectors.toSet()); + + configuration.setExcludeFieldsWithModifiers(modifiers); + } return configuration; } @@ -225,6 +293,14 @@ public GsonBuilder builder() { .setLongSerializationPolicy(getLongSerializationPolicy()) .setFieldNamingPolicy(getFieldNamingPolicy()); + final int[] modifiers = getExcludeFieldsWithModifiers().stream() + .mapToInt(m -> m.modifier) + .toArray(); + + builder.excludeFieldsWithModifiers(modifiers); + + if (isExcludeFieldsWithoutExposeAnnotation()) + builder.excludeFieldsWithoutExposeAnnotation(); if (isComplexMapKeySerialization()) builder.enableComplexMapKeySerialization(); if (isGenerateNonExecutableJson()) @@ -423,6 +499,57 @@ public GsonConfiguration setSerializeSpecialFloatingPointValues(boolean serializ return this; } + public boolean isExcludeFieldsWithoutExposeAnnotation() { + return excludeFieldsWithoutExposeAnnotation; + } + + /** + * Configures Gson to exclude all fields from consideration for serialization or deserialization + * that do not have the {@link com.google.gson.annotations.Expose} annotation. + * + * @see GsonBuilder#excludeFieldsWithoutExposeAnnotation() + * @param excludeFieldsWithoutExposeAnnotation set true to exclude + * @return self + */ + public GsonConfiguration setExcludeFieldsWithoutExposeAnnotation(boolean excludeFieldsWithoutExposeAnnotation) { + this.excludeFieldsWithoutExposeAnnotation = excludeFieldsWithoutExposeAnnotation; + return this; + } + + public Set getExcludeFieldsWithModifiers() { + return excludeFieldsWithModifiers; + } + + /** + * Configures Gson to excludes all class fields that have the specified modifiers. By default, + * Gson will exclude all fields marked transient or static. This method will override that + * behavior. + * + * @see GsonBuilder#excludeFieldsWithModifiers(int...) + * @param excludeFieldsWithModifiers fields modifiers to exclude + * @return self + */ + public GsonConfiguration setExcludeFieldsWithModifiers(Set excludeFieldsWithModifiers) { + this.excludeFieldsWithModifiers = Collections.unmodifiableSet(excludeFieldsWithModifiers); + return this; + } + + /** + * Configures Gson to excludes all class fields that have the specified modifiers. By default, + * Gson will exclude all fields marked transient or static. This method will override that + * behavior. + * + * @see GsonBuilder#excludeFieldsWithModifiers(int...) + * @param excludeFieldsWithModifiers fields modifiers to exclude + * @return self + */ + public GsonConfiguration setExcludeFieldsWithModifiers(FieldModifiers... excludeFieldsWithModifiers) { + this.excludeFieldsWithModifiers = Collections.unmodifiableSet(Arrays.stream(excludeFieldsWithModifiers) + .collect(Collectors.toSet())); + + return this; + } + public DateTimeFormatter getInstantFormat() { return instantFormat; } diff --git a/src/main/java/io/goodforgod/gson/configuration/GsonProperties.java b/src/main/java/io/goodforgod/gson/configuration/GsonProperties.java index 9016398..3a9f520 100644 --- a/src/main/java/io/goodforgod/gson/configuration/GsonProperties.java +++ b/src/main/java/io/goodforgod/gson/configuration/GsonProperties.java @@ -32,6 +32,8 @@ private GsonProperties() {} public static final String GENERATE_NON_EXECUTABLE_JSON = PREFIX + "generateNonExecutableJson"; public static final String COMPLEX_MAP_KEY_SERIALIZATION = PREFIX + "serializeComplexMapKey"; public static final String SERIALIZE_SPECIAL_FLOATING_POINT_VALUES = PREFIX + "serializeSpecialFloatingPointValues"; + public static final String EXCLUDE_FIELDS_WITHOUT_EXPOSE_ANNOTATION = PREFIX + "excludeFieldsWithoutExposeAnnotation"; + public static final String EXCLUDE_FIELDS_WITH_MODIFIERS = PREFIX + "excludeFieldsWithModifiers"; public static final String POLICY_FIELD_NAMING = PREFIX + "policy.fieldNaming"; public static final String POLICY_LONG_SERIALIZATION = PREFIX + "policy.longSerialization"; diff --git a/src/test/java/io/goodforgod/gson/configuration/GsonConfigurationFromPropertiesTests.java b/src/test/java/io/goodforgod/gson/configuration/GsonConfigurationFromPropertiesTests.java index 31097d3..0484b63 100644 --- a/src/test/java/io/goodforgod/gson/configuration/GsonConfigurationFromPropertiesTests.java +++ b/src/test/java/io/goodforgod/gson/configuration/GsonConfigurationFromPropertiesTests.java @@ -41,6 +41,9 @@ void configurationPropertiesApplied() throws Exception { assertTrue(configuration.isGenerateNonExecutableJson()); assertTrue(configuration.isSerializeSpecialFloatingPointValues()); assertFalse(configuration.isEscapeHtmlChars()); + assertTrue(configuration.isExcludeFieldsWithoutExposeAnnotation()); + assertEquals(1, configuration.getExcludeFieldsWithModifiers().size()); + assertTrue(configuration.getExcludeFieldsWithModifiers().contains(GsonConfiguration.FieldModifiers.TRANSIENT)); assertEquals(FieldNamingPolicy.UPPER_CAMEL_CASE, configuration.getFieldNamingPolicy()); assertEquals(LongSerializationPolicy.STRING, configuration.getLongSerializationPolicy()); @@ -70,6 +73,12 @@ void configurationPropertiesAsDefault() { assertFalse(configuration.isGenerateNonExecutableJson()); assertFalse(configuration.isSerializeSpecialFloatingPointValues()); assertTrue(configuration.isEscapeHtmlChars()); + assertFalse(configuration.isExcludeFieldsWithoutExposeAnnotation()); + assertEquals(4, configuration.getExcludeFieldsWithModifiers().size()); + assertTrue(configuration.getExcludeFieldsWithModifiers().contains(GsonConfiguration.FieldModifiers.TRANSIENT)); + assertTrue(configuration.getExcludeFieldsWithModifiers().contains(GsonConfiguration.FieldModifiers.STATIC)); + assertTrue(configuration.getExcludeFieldsWithModifiers().contains(GsonConfiguration.FieldModifiers.VOLATILE)); + assertTrue(configuration.getExcludeFieldsWithModifiers().contains(GsonConfiguration.FieldModifiers.SYNCHRONIZED)); assertEquals(FieldNamingPolicy.IDENTITY, configuration.getFieldNamingPolicy()); assertEquals(LongSerializationPolicy.DEFAULT, configuration.getLongSerializationPolicy()); diff --git a/src/test/resources/gson.properties b/src/test/resources/gson.properties index e64022a..e357d68 100644 --- a/src/test/resources/gson.properties +++ b/src/test/resources/gson.properties @@ -20,6 +20,8 @@ gson.escapeHtmlChars=false gson.generateNonExecutableJson=true gson.serializeComplexMapKey=true gson.serializeSpecialFloatingPointValues=true +gson.excludeFieldsWithoutExposeAnnotation=true +gson.excludeFieldsWithModifiers=TRANSIENT gson.policy.fieldNaming=UPPER_CAMEL_CASE gson.policy.longSerialization=STRING From d91e8672cb59187ec89cef2cc11eaefd97c33b65 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Wed, 9 Mar 2022 01:11:43 +0300 Subject: [PATCH 2/2] [1.4.0-SNAPSHOT] README.md updated --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1f102ca..e6a0cb0 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Gson configuration and serializers/deserializers for Date/Time in [java.time.*]( ## Dependency :rocket: **Gradle** ```groovy -implementation "io.goodforgod:gson-configuration:1.3.0" +implementation "io.goodforgod:gson-configuration:1.4.0" ``` **Maven** @@ -19,7 +19,7 @@ implementation "io.goodforgod:gson-configuration:1.3.0" io.goodforgod gson-configuration - 1.3.0 + 1.4.0 ``` @@ -83,6 +83,8 @@ GsonBuilder builder = new GsonConfiguration() .setGenerateNonExecutableJson(true) .setSerializeNulls(true) .setSerializeSpecialFloatingPointValues(true) + .setExcludeFieldsWithoutExposeAnnotation(true) + .setExcludeFieldsWithModifiers(GsonConfiguration.FieldModifiers.TRANSIENT) .builder(); ``` @@ -95,6 +97,9 @@ GsonBuilder builder = new GsonConfiguration() ### Properties file +**By default** Gson or GsonBuilder that is build via GsonConfiguration or GsonFactory **doesn't serialize/deserialize** transient, static, volatile, synchronized fields. +You need to use configuration setters to configure it otherwise. + GsonConfiguration also can be filled from *properties* file. How to build GsonConfiguration from Properties: @@ -130,6 +135,8 @@ gson.escapeHtmlChars=false gson.generateNonExecutableJson=true gson.serializeComplexMapKey=true gson.serializeSpecialFloatingPointValues=true +gson.excludeFieldsWithoutExposeAnnotation=false +gson.excludeFieldsWithModifiers=TRANSIENT,STATIC,SYNCHRONIZED,VOLATILE gson.policy.fieldNaming=UPPER_CAMEL_CASE gson.policy.longSerialization=STRING