diff options
-rw-r--r-- | .travis.yml | 13 | ||||
-rw-r--r-- | pom.xml | 52 | ||||
-rw-r--r-- | release-notes/VERSION-2.x | 8 | ||||
-rw-r--r-- | src/main/java/com/fasterxml/jackson/annotation/JsonIncludeProperties.java | 164 | ||||
-rw-r--r-- | src/main/java/com/fasterxml/jackson/annotation/JsonSubTypes.java | 17 | ||||
-rw-r--r-- | src/test/java/com/fasterxml/jackson/annotation/JsonIncludePropertiesTest.java | 68 |
6 files changed, 300 insertions, 22 deletions
diff --git a/.travis.yml b/.travis.yml index cc6344e..9b7656c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,16 +9,9 @@ jdk: # Below this line is configuration for deploying to the Sonatype OSS repo # http://blog.xeiam.com/2013/05/configure-travis-ci-to-deploy-snapshots.html before_install: "git clone -b travis `git config --get remote.origin.url` target/travis" -after_success: "mvn deploy --settings target/travis/settings.xml" - -# whitelist -branches: - only: - - master - - 2.11 +script: "[ ${TRAVIS_PULL_REQUEST} = 'false' ] && mvn -B source:jar deploy --settings target/travis/settings.xml || mvn clean verify --settings target/travis/settings.xml" env: global: - - secure: "a2T/bJVacCzBKfGW7ycW2aVLxupCUenLpRX6Neh/WLFf5wbIjTtKZpB94sxbVGTKNT+HQmwFynWhz7Dh3251fSZ4lfGcHBnUr5lpq1vo10aMBTJaMpk5vrTLn0AxGESxqOjQQkDhasdLpoXlv1EtVn7HqHLepAr0AIUl41XggfA=" - - secure: "Gdl5m/gEm4FrCxOVnvCM13dipEhjaL4IXbEL2dsNhxwZ+lqD+8OAwObbPrTLuUZ+KTWctfsNauGTin1bDhi8m/Gh6jm71U+hGuA7/8cqgprZDhdARxG7fSOdr9Syp24JL7h5u5X43+a7m2KiC/iUcErIiVKPBcAisOj286GcFhc=" - + - secure: "BmDMUlS07UDn3zGHra90YOT+U2QpTRmb02GIJmvfykmOie2q8hP6XwxKehsYBtnMHp6+5xm+bF+dA4BhjJ9l+bzXIH2FVkFTfJEo1GJJiVGq8B1DptqGGl404Eb4vD4c+cbb4GP2zKgNMc05oNsKTThE3wHdxZDbh2hjATz/b0M=" + - secure: "XcMnwfSjRNVHs3HEutOIIGyYiHUZQZx2+DynlzkiBZ3IKpiVT0LfeIElJfWKJkwtJxGIIJghNZArylKvJXeZguB4s0XieOHxzarE1BgfcYU+vK/0pla0x66mgGEPjD/99arrGTTvZUH//jmRPc95fZJqytdIOJ7Erb4GpGLo/hM=" @@ -1,17 +1,22 @@ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <!-- This module was also published with a richer model, Gradle metadata, --> + <!-- which should be used instead. Do not delete the following line which --> + <!-- is to indicate to Gradle or any Gradle module metadata file consumer --> + <!-- that they should prefer consuming it instead. --> + <!-- do_not_remove: published-with-gradle-metadata --> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.fasterxml.jackson</groupId> <!-- this is one of few Jackson modules that depends on parent and NOT jackson-bom --> <artifactId>jackson-parent</artifactId> - <version>2.11</version> + <version>2.12-SNAPSHOT</version> </parent> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <name>Jackson-annotations</name> - <version>2.11.2-SNAPSHOT</version> + <version>2.12.0-SNAPSHOT</version> <packaging>bundle</packaging> <description>Core annotations used for value types, used by Jackson data binding package. </description> @@ -43,12 +48,6 @@ <maven.compiler.target>1.6</maven.compiler.target> <osgi.export>com.fasterxml.jackson.annotation.*;version=${project.version}</osgi.export> - - <!-- 31-Jul-2020, tatu: will go in `oss-parent` for 2.12, but for 2.11 need to bump - bundle-plugin version to make work in JDK 15+ - (see https://github.com/FasterXML/jackson-databind/issues/2806) - --> - <version.plugin.bundle>5.1.1</version.plugin.bundle> </properties> <dependencies> @@ -59,6 +58,18 @@ </dependency> </dependencies> + <!-- Alas, need to include snapshot reference since otherwise can not find + snapshot of parent... --> + <repositories> + <repository> + <id>sonatype-nexus-snapshots</id> + <name>Sonatype Nexus Snapshots</name> + <url>https://oss.sonatype.org/content/repositories/snapshots</url> + <releases><enabled>false</enabled></releases> + <snapshots><enabled>true</enabled></snapshots> + </repository> + </repositories> + <build> <plugins> <plugin> @@ -98,6 +109,31 @@ <stagingProfileId>b34f19b9cc6224</stagingProfileId> </configuration> </plugin> + + <!-- 11-Jun-2020, tatu: As per [annotations#173], add gradle module metadata + --> + <plugin> + <groupId>de.jjohannes</groupId> + <artifactId>gradle-module-metadata-maven-plugin</artifactId> + <version>0.2.0</version> + <executions> + <execution> + <goals> + <goal>gmm</goal> + </goals> + </execution> + </executions> + <configuration> + <platformDependencies> + <dependency> + <groupId>com.fasterxml.jackson</groupId> + <artifactId>jackson-bom</artifactId> + <version>${project.version}</version> + </dependency> + </platformDependencies> + </configuration> + </plugin> + </plugins> </build> diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 43b508f..74e64de 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -11,6 +11,14 @@ NOTE: Annotations module will never contain changes in patch versions, === Releases === ------------------------------------------------------------------------ +2.12.0 (not yet released) + +#171: `JsonSubType.Type` should accept array of names + (contributed by Swayam R) +#173: Jackson version alignment with Gradle 6 +#174: Add `@JsonIncludeProperties` + (contributed by Baptiste P) + 2.11.0 (26-Apr-2020) - `JsonPattern.Value.pattern` retained as "", never (accidentally) exposed diff --git a/src/main/java/com/fasterxml/jackson/annotation/JsonIncludeProperties.java b/src/main/java/com/fasterxml/jackson/annotation/JsonIncludeProperties.java new file mode 100644 index 0000000..a1f5fcd --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/annotation/JsonIncludeProperties.java @@ -0,0 +1,164 @@ +package com.fasterxml.jackson.annotation; + +import java.lang.annotation.*; +import java.util.*; + +/** + * Annotation that can be used to either only include serialization of + * properties (during serialization), or only include processing of + * JSON properties read (during deserialization). + * <p> + * Example: + * <pre> + * // to only include specified fields from being serialized or deserialized + * // (i.e. only include in JSON output; or being set even if they were included) + * @JsonIncludeProperties({ "internalId", "secretKey" }) + * </pre> + * <p> + * Annotation can be applied both to classes and + * to properties. If used for both, actual set will be union of all + * includes: that is, you can only add properties to include, not remove + * or override. So you can not remove properties to include using + * per-property annotation. + * + * @since 2.12 + */ +@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, + ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@JacksonAnnotation +public @interface JsonIncludeProperties +{ + /** + * Names of properties to include. + */ + public String[] value() default {}; + + /* + /********************************************************** + /* Value class used to enclose information, allow for + /* merging of layered configuration settings. + /********************************************************** + */ + + /** + * Helper class used to contain information from a single {@link JsonIncludeProperties} + * annotation, as well as to provide possible overrides from non-annotation sources. + * + * @since 2.12 + */ + public static class Value implements JacksonAnnotationValue<JsonIncludeProperties>, java.io.Serializable + { + private static final long serialVersionUID = 1L; + + /** + * Default instance has no explicitly included fields + */ + protected final static JsonIncludeProperties.Value ALL = new JsonIncludeProperties.Value(null); + + /** + * Name of the properties to include. + * Null means that all properties are included, empty means none. + */ + protected final Set<String> _included; + + protected Value(Set<String> included) + { + _included = included; + } + + public static JsonIncludeProperties.Value from(JsonIncludeProperties src) + { + if (src == null) { + return ALL; + } + return new Value(_asSet(src.value())); + } + + public static JsonIncludeProperties.Value all() + { + return ALL; + } + + @Override + public Class<JsonIncludeProperties> valueFor() + { + return JsonIncludeProperties.class; + } + + /** + * @return Names included, if any, possibly empty; {@code null} for "not defined" + */ + public Set<String> getIncluded() + { + return _included; + } + + /** + * Mutant factory method to override the current value with an another, + * merging the included fields so that only entries that exist in both original + * and override set are included, taking into account that "undefined" {@link Value}s + * do not count ("undefined" meaning that {@code getIncluded()} returns {@code null}). + * So: overriding with "undefined" returns original {@code Value} as-is; overriding an + * "undefined" {@code Value} returns override {@code Value} as-is. + */ + public JsonIncludeProperties.Value withOverrides(JsonIncludeProperties.Value overrides) + { + final Set<String> otherIncluded; + + if (overrides == null || (otherIncluded = overrides.getIncluded()) == null) { + return this; + } + + if (_included == null) { + return overrides; + } + + HashSet<String> toInclude = new HashSet<String>(); + for (String incl : otherIncluded) { + if (_included.contains(incl)) { + toInclude.add(incl); + } + } + + return new JsonIncludeProperties.Value(toInclude); + } + + @Override + public String toString() { + return String.format("JsonIncludeProperties.Value(included=%s)", + _included); + } + + @Override + public int hashCode() { + return (_included == null) ? 0 : _included.size(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null) return false; + return (o.getClass() == getClass()) && _equals(_included, ((Value) o)._included); + } + + private static boolean _equals(Set<String> a, Set<String> b) + { + return a == null ? (b == null) + // keep this last just because it can be expensive + : a.equals(b); + } + + private static Set<String> _asSet(String[] v) + { + if (v == null || v.length == 0) { + return Collections.emptySet(); + } + Set<String> s = new HashSet<String>(v.length); + for (String str : v) { + s.add(str); + } + return s; + } + } +} diff --git a/src/main/java/com/fasterxml/jackson/annotation/JsonSubTypes.java b/src/main/java/com/fasterxml/jackson/annotation/JsonSubTypes.java index 8263038..9771bba 100644 --- a/src/main/java/com/fasterxml/jackson/annotation/JsonSubTypes.java +++ b/src/main/java/com/fasterxml/jackson/annotation/JsonSubTypes.java @@ -28,9 +28,9 @@ public @interface JsonSubTypes { public Type[] value(); /** - * Definition of a subtype, along with optional name. If name is missing, class - * of the type will be checked for {@link JsonTypeName} annotation; and if that - * is also missing or empty, a default + * Definition of a subtype, along with optional name(s). If no name is defined + * (empty Strings are ignored), class of the type will be checked for {@link JsonTypeName} + * annotation; and if that is also missing or empty, a default * name will be constructed by type id mechanism. * Default name is usually based on class name. */ @@ -41,8 +41,17 @@ public @interface JsonSubTypes { public Class<?> value(); /** - * Logical type name used as the type identifier for the class + * Logical type name used as the type identifier for the class, if defined; empty + * String means "not defined". Used unless {@link #names} is defined as non-empty. */ public String name() default ""; + + /** + * (optional) Logical type names used as the type identifier for the class: used if + * more than one type name should be associated with the same type. + * + * @since 2.12 + */ + public String[] names() default {}; } } diff --git a/src/test/java/com/fasterxml/jackson/annotation/JsonIncludePropertiesTest.java b/src/test/java/com/fasterxml/jackson/annotation/JsonIncludePropertiesTest.java new file mode 100644 index 0000000..55ae738 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/annotation/JsonIncludePropertiesTest.java @@ -0,0 +1,68 @@ +package com.fasterxml.jackson.annotation; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Tests to verify that it is possibly to merge {@link JsonIncludeProperties.Value} + * instances for overrides + */ +public class JsonIncludePropertiesTest extends TestBase +{ + @JsonIncludeProperties(value = {"foo", "bar"}) + private final static class Bogus + { + } + + private final JsonIncludeProperties.Value ALL = JsonIncludeProperties.Value.all(); + + public void testAll() + { + assertSame(ALL, JsonIncludeProperties.Value.from(null)); + assertNull(ALL.getIncluded()); + assertEquals(ALL, ALL); + assertEquals("JsonIncludeProperties.Value(included=null)", ALL.toString()); + assertEquals(0, ALL.hashCode()); + } + + public void testFromAnnotation() + { + JsonIncludeProperties.Value v = JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class)); + assertNotNull(v); + Set<String> included = v.getIncluded(); + assertEquals(2, v.getIncluded().size()); + assertEquals(_set("foo", "bar"), included); + assertEquals("JsonIncludeProperties.Value(included=[bar, foo])", v.toString()); + assertEquals(v, JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class))); + } + + public void testWithOverridesAll() { + JsonIncludeProperties.Value v = JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class)); + v = v.withOverrides(ALL); + Set<String> included = v.getIncluded(); + assertEquals(2, included.size()); + assertEquals(_set("foo", "bar"), included); + } + + public void testWithOverridesEmpty() { + JsonIncludeProperties.Value v = JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class)); + v = v.withOverrides(new JsonIncludeProperties.Value(Collections.<String>emptySet())); + Set<String> included = v.getIncluded(); + assertEquals(0, included.size()); + } + + public void testWithOverridesMerge() { + JsonIncludeProperties.Value v = JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class)); + v = v.withOverrides(new JsonIncludeProperties.Value(_set("foo"))); + Set<String> included = v.getIncluded(); + assertEquals(1, included.size()); + assertEquals(_set("foo"), included); + } + + private Set<String> _set(String... args) + { + return new LinkedHashSet<String>(Arrays.asList(args)); + } +} |