#1151 - Make sure we explicitly expose all media type model properties.

Jackson can be configured to not auto-detect properties on objects to be rendered, requiring them to be explicitly annotated with @JsonProperty to be exposed. In such a configuration setup, some of our model types do not work properly as so far we have expected public properties to be included automatically.

This commit changes that to explicitly include @JsonProperty on all of the getter methods exposed.
This commit is contained in:
Oliver Drotbohm
2019-12-09 09:38:19 +01:00
parent 285af0c7e4
commit e51a7f7a28
11 changed files with 41 additions and 12 deletions

View File

@@ -37,6 +37,7 @@ import org.springframework.util.StringUtils;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* Value object for links. * Value object for links.
@@ -46,9 +47,9 @@ import com.fasterxml.jackson.annotation.JsonInclude;
* @author Jens Schauder * @author Jens Schauder
*/ */
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(value = "templated", ignoreUnknown = true) @JsonIgnoreProperties(value = { "templated", "template" }, ignoreUnknown = true)
@AllArgsConstructor(access = AccessLevel.PACKAGE) @AllArgsConstructor(access = AccessLevel.PACKAGE)
@Getter @Getter(onMethod = @__(@JsonProperty))
@EqualsAndHashCode( @EqualsAndHashCode(
of = { "rel", "href", "hreflang", "media", "title", "type", "deprecation", "profile", "name", "affordances" }) of = { "rel", "href", "hreflang", "media", "title", "type", "deprecation", "profile", "name", "affordances" })
public class Link implements Serializable { public class Link implements Serializable {

View File

@@ -16,6 +16,7 @@
package org.springframework.hateoas.mediatype.collectionjson; package org.springframework.hateoas.mediatype.collectionjson;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter;
import lombok.Value; import lombok.Value;
import lombok.experimental.Wither; import lombok.experimental.Wither;
@@ -39,6 +40,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @author Greg Turnquist * @author Greg Turnquist
*/ */
@Value @Value
@Getter(onMethod = @__(@JsonProperty))
@Wither(AccessLevel.PACKAGE) @Wither(AccessLevel.PACKAGE)
class CollectionJson<T> { class CollectionJson<T> {

View File

@@ -15,13 +15,13 @@
*/ */
package org.springframework.hateoas.mediatype.collectionjson; package org.springframework.hateoas.mediatype.collectionjson;
import lombok.Getter;
import lombok.Value; import lombok.Value;
import lombok.experimental.Wither; import lombok.experimental.Wither;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@@ -30,17 +30,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @author Greg Turnquist * @author Greg Turnquist
*/ */
@Value @Value
@Getter(onMethod = @__(@JsonProperty))
@JsonInclude(Include.NON_NULL)
@Wither @Wither
@JsonIgnoreProperties()
class CollectionJsonData { class CollectionJsonData {
@JsonInclude(Include.NON_NULL) //
private @Nullable String name; private @Nullable String name;
@JsonInclude(Include.NON_NULL) //
private @Nullable Object value; private @Nullable Object value;
@JsonInclude(Include.NON_NULL) //
private @Nullable String prompt; private @Nullable String prompt;
@JsonCreator @JsonCreator

View File

@@ -16,6 +16,7 @@
package org.springframework.hateoas.mediatype.collectionjson; package org.springframework.hateoas.mediatype.collectionjson;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Value; import lombok.Value;
import lombok.experimental.Wither; import lombok.experimental.Wither;
@@ -33,6 +34,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @author Greg Turnquist * @author Greg Turnquist
*/ */
@Value @Value
@Getter(onMethod = @__(@JsonProperty))
@Wither(AccessLevel.PACKAGE) @Wither(AccessLevel.PACKAGE)
@RequiredArgsConstructor(access = AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PACKAGE)
class CollectionJsonDocument<T> { class CollectionJsonDocument<T> {

View File

@@ -45,6 +45,7 @@ import com.fasterxml.jackson.databind.JavaType;
* @author Greg Turnquist * @author Greg Turnquist
*/ */
@Value @Value
@Getter(onMethod = @__(@JsonProperty))
@Wither(AccessLevel.PACKAGE) @Wither(AccessLevel.PACKAGE)
@RequiredArgsConstructor(access = AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PACKAGE)
class CollectionJsonItem<T> { class CollectionJsonItem<T> {
@@ -79,6 +80,7 @@ class CollectionJsonItem<T> {
* *
* @return * @return
*/ */
@JsonProperty
public List<CollectionJsonData> getData() { public List<CollectionJsonData> getData() {
if (!this.data.isEmpty()) { if (!this.data.isEmpty()) {

View File

@@ -17,6 +17,7 @@ package org.springframework.hateoas.mediatype.hal.forms;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.NonNull; import lombok.NonNull;
import lombok.ToString; import lombok.ToString;
@@ -29,6 +30,7 @@ import org.springframework.hateoas.AffordanceModel.PropertyMetadataConfigured;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* Describe a parameter for the associated state transition in a HAL-FORMS document. A {@link HalFormsTemplate} may * Describe a parameter for the associated state transition in a HAL-FORMS document. A {@link HalFormsTemplate} may
@@ -39,6 +41,7 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(Include.NON_DEFAULT) @JsonInclude(Include.NON_DEFAULT)
@Value @Value
@Wither @Wither
@Getter(onMethod = @__(@JsonProperty))
@AllArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(force = true) @NoArgsConstructor(force = true)
@ToString @ToString

View File

@@ -16,6 +16,7 @@
package org.springframework.hateoas.mediatype.uber; package org.springframework.hateoas.mediatype.uber;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter;
import lombok.Value; import lombok.Value;
import lombok.experimental.Wither; import lombok.experimental.Wither;
@@ -37,6 +38,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @since 1.0 * @since 1.0
*/ */
@Value @Value
@Getter(onMethod = @__(@JsonProperty))
@Wither(AccessLevel.PACKAGE) @Wither(AccessLevel.PACKAGE)
@JsonInclude(Include.NON_NULL) @JsonInclude(Include.NON_NULL)
class Uber { class Uber {
@@ -74,5 +76,4 @@ class Uber {
.flatMap(uberData -> uberData.getLinks().stream()) // .flatMap(uberData -> uberData.getLinks().stream()) //
.collect(Links.collector()); .collect(Links.collector());
} }
} }

View File

@@ -17,6 +17,7 @@ package org.springframework.hateoas.mediatype.uber;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import lombok.Value; import lombok.Value;
import lombok.experimental.Wither; import lombok.experimental.Wither;
@@ -42,6 +43,7 @@ import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.mediatype.PropertyUtils; import org.springframework.hateoas.mediatype.PropertyUtils;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
@@ -58,6 +60,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @since 1.0 * @since 1.0
*/ */
@Value @Value
@Getter(onMethod = @__(@JsonProperty))
@Wither(AccessLevel.PACKAGE) @Wither(AccessLevel.PACKAGE)
@JsonInclude(Include.NON_NULL) @JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@@ -105,6 +108,7 @@ class UberData {
* Don't render if it's {@link UberAction#READ}. * Don't render if it's {@link UberAction#READ}.
*/ */
@Nullable @Nullable
@JsonProperty
public UberAction getAction() { public UberAction getAction() {
return action == UberAction.READ ? null : action; return action == UberAction.READ ? null : action;
} }
@@ -113,6 +117,7 @@ class UberData {
* Use a {@link Boolean} to support returning {@literal null}, and if it is {@literal null}, don't render. * Use a {@link Boolean} to support returning {@literal null}, and if it is {@literal null}, don't render.
*/ */
@Nullable @Nullable
@JsonProperty
public Boolean isTemplated() { public Boolean isTemplated() {
return Optional.ofNullable(this.url) // return Optional.ofNullable(this.url) //
@@ -124,6 +129,7 @@ class UberData {
* Use a {@link Boolean} to support returning {@literal null}, and if it is {@literal null}, don't render. * Use a {@link Boolean} to support returning {@literal null}, and if it is {@literal null}, don't render.
*/ */
@Nullable @Nullable
@JsonProperty
public Boolean isTransclude() { public Boolean isTransclude() {
return this.transclude ? true : null; return this.transclude ? true : null;
} }
@@ -147,6 +153,10 @@ class UberData {
.orElse(Collections.emptyList()); .orElse(Collections.emptyList());
} }
private boolean hasUrl(@Nullable String url) {
return ObjectUtils.nullSafeEquals(this.url, url);
}
/** /**
* Simple scalar types that can be encoded by value, not type. * Simple scalar types that can be encoded by value, not type.
*/ */
@@ -363,7 +373,7 @@ class UberData {
return affordanceBasedLinks.stream() // return affordanceBasedLinks.stream() //
.flatMap(affordance -> links.stream() // .flatMap(affordance -> links.stream() //
.filter(link -> link.getUrl().equals(affordance.getUrl())) // .filter(data -> data.hasUrl(affordance.getUrl())) //
.map(link -> { .map(link -> {
if (link.getAction() == affordance.getAction()) { if (link.getAction() == affordance.getAction()) {

View File

@@ -16,6 +16,7 @@
package org.springframework.hateoas.mediatype.uber; package org.springframework.hateoas.mediatype.uber;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Value; import lombok.Value;
import lombok.experimental.Wither; import lombok.experimental.Wither;
@@ -34,6 +35,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @since 1.0 * @since 1.0
*/ */
@Value @Value
@Getter(onMethod = @__(@JsonProperty))
@Wither(AccessLevel.PACKAGE) @Wither(AccessLevel.PACKAGE)
@RequiredArgsConstructor(access = AccessLevel.PACKAGE) @RequiredArgsConstructor(access = AccessLevel.PACKAGE)
class UberDocument { class UberDocument {

View File

@@ -20,11 +20,12 @@ import java.io.Writer;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
/** /**
* Base class to test objects against the Jackson 2.0 {@link ObjectMapper}. * Base class to test objects against the Jackson 2.0 {@link ObjectMapper}.
* *
* @author Oliver Gierke * @author Oliver Gierke
* @author Jon Brisbin * @author Jon Brisbin
* @author Greg Turnquist * @author Greg Turnquist
@@ -36,6 +37,11 @@ public abstract class AbstractJackson2MarshallingIntegrationTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
mapper = new ObjectMapper(); mapper = new ObjectMapper();
mapper.disable(MapperFeature.AUTO_DETECT_CREATORS) //
.disable(MapperFeature.AUTO_DETECT_FIELDS) //
.disable(MapperFeature.AUTO_DETECT_GETTERS) //
.disable(MapperFeature.AUTO_DETECT_IS_GETTERS) //
.disable(MapperFeature.AUTO_DETECT_SETTERS);
} }
protected String write(Object object) throws Exception { protected String write(Object object) throws Exception {

View File

@@ -2,9 +2,13 @@ package org.springframework.hateoas.mediatype.hal;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import com.fasterxml.jackson.annotation.JsonProperty;
@Data @Data
@Getter(onMethod = @__(@JsonProperty))
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class SimplePojo { public class SimplePojo {