From 7beaa606e28ef54d92e0b1831df6c229c560ea96 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 4 Sep 2018 14:54:10 -0400 Subject: [PATCH] GH-794: Json Deserializer - remove type headers Resolves https://github.com/spring-projects/spring-kafka/issues/794 --- .../DefaultJackson2JavaTypeMapper.java | 15 ++++++++++++ .../converter/Jackson2JavaTypeMapper.java | 9 ++++++++ .../support/serializer/JsonDeserializer.java | 23 +++++++++++++++++++ .../KafkaMessageListenerContainerTests.java | 1 + src/reference/asciidoc/kafka.adoc | 4 ++++ src/reference/asciidoc/whats-new.adoc | 2 ++ 6 files changed, 54 insertions(+) diff --git a/spring-kafka/src/main/java/org/springframework/kafka/support/converter/DefaultJackson2JavaTypeMapper.java b/spring-kafka/src/main/java/org/springframework/kafka/support/converter/DefaultJackson2JavaTypeMapper.java index 0c424d95..fdb6a6a8 100644 --- a/spring-kafka/src/main/java/org/springframework/kafka/support/converter/DefaultJackson2JavaTypeMapper.java +++ b/spring-kafka/src/main/java/org/springframework/kafka/support/converter/DefaultJackson2JavaTypeMapper.java @@ -182,4 +182,19 @@ public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper return toJavaType(headers).getRawClass(); } + @Override + public void removeHeaders(Headers headers) { + try { + headers.remove(getClassIdFieldName()); + headers.remove(getContentClassIdFieldName()); + headers.remove(getKeyClassIdFieldName()); + headers.remove(KEY_DEFAULT_CLASSID_FIELD_NAME); + headers.remove(KEY_DEFAULT_CONTENT_CLASSID_FIELD_NAME); + headers.remove(KEY_DEFAULT_KEY_CLASSID_FIELD_NAME); + } + catch (Exception e) { + // NOSONAR + } + } + } diff --git a/spring-kafka/src/main/java/org/springframework/kafka/support/converter/Jackson2JavaTypeMapper.java b/spring-kafka/src/main/java/org/springframework/kafka/support/converter/Jackson2JavaTypeMapper.java index ce15c1cf..c54f985f 100644 --- a/spring-kafka/src/main/java/org/springframework/kafka/support/converter/Jackson2JavaTypeMapper.java +++ b/spring-kafka/src/main/java/org/springframework/kafka/support/converter/Jackson2JavaTypeMapper.java @@ -68,4 +68,13 @@ public interface Jackson2JavaTypeMapper extends ClassMapper { void addTrustedPackages(String... packages); + /** + * Remove the type information headers. + * @param headers the headers. + * @since 2.2 + */ + default void removeHeaders(Headers headers) { + // NOSONAR + } + } diff --git a/spring-kafka/src/main/java/org/springframework/kafka/support/serializer/JsonDeserializer.java b/spring-kafka/src/main/java/org/springframework/kafka/support/serializer/JsonDeserializer.java index 91829b39..e335eb5e 100644 --- a/spring-kafka/src/main/java/org/springframework/kafka/support/serializer/JsonDeserializer.java +++ b/spring-kafka/src/main/java/org/springframework/kafka/support/serializer/JsonDeserializer.java @@ -89,6 +89,11 @@ public class JsonDeserializer implements ExtendedDeserializer { */ public static final String TYPE_MAPPINGS = JsonSerializer.TYPE_MAPPINGS; + /** + * Kafka config property for removing type headers. + */ + public static final String REMOVE_TYPE_INFO_HEADERS = "spring.json.remove.type.headers"; + protected final ObjectMapper objectMapper; protected Class targetType; @@ -99,6 +104,8 @@ public class JsonDeserializer implements ExtendedDeserializer { private boolean typeMapperExplicitlySet = false; + private boolean removeTypeHeaders = true; + /** * Construct an instance with a default {@link ObjectMapper}. */ @@ -203,6 +210,16 @@ public class JsonDeserializer implements ExtendedDeserializer { } } + /** + * Set to false to retain type information headers after deserialization. + * Default true. + * @param removeTypeHeaders true to remove headers. + * @since 2.1 + */ + public void setRemoveTypeHeaders(boolean removeTypeHeaders) { + this.removeTypeHeaders = removeTypeHeaders; + } + @SuppressWarnings("unchecked") @Override public void configure(Map configs, boolean isKey) { @@ -263,6 +280,9 @@ public class JsonDeserializer implements ExtendedDeserializer { ((AbstractJavaTypeMapper) this.typeMapper).setIdClassMapping( JsonSerializer.createMappings((String) configs.get(JsonSerializer.TYPE_MAPPINGS))); } + if (configs.containsKey(REMOVE_TYPE_INFO_HEADERS)) { + this.removeTypeHeaders = Boolean.parseBoolean((String) configs.get(REMOVE_TYPE_INFO_HEADERS)); + } } /** @@ -292,6 +312,9 @@ public class JsonDeserializer implements ExtendedDeserializer { reader = this.objectMapper.readerFor(javaType); } } + if (this.removeTypeHeaders) { + this.typeMapper.removeHeaders(headers); + } if (reader == null) { reader = this.reader; } diff --git a/spring-kafka/src/test/java/org/springframework/kafka/listener/KafkaMessageListenerContainerTests.java b/spring-kafka/src/test/java/org/springframework/kafka/listener/KafkaMessageListenerContainerTests.java index 42337023..cdbff98a 100644 --- a/spring-kafka/src/test/java/org/springframework/kafka/listener/KafkaMessageListenerContainerTests.java +++ b/spring-kafka/src/test/java/org/springframework/kafka/listener/KafkaMessageListenerContainerTests.java @@ -1647,6 +1647,7 @@ public class KafkaMessageListenerContainerTests { assertThat(received.get().value()).isInstanceOf(Foo.class); container.stop(); this.logger.info("Stop JSON2"); + assertThat(received.get().headers().iterator().hasNext()).isFalse(); } @Test diff --git a/src/reference/asciidoc/kafka.adoc b/src/reference/asciidoc/kafka.adoc index c866c310..d892582c 100644 --- a/src/reference/asciidoc/kafka.adoc +++ b/src/reference/asciidoc/kafka.adoc @@ -1629,11 +1629,15 @@ In addition, the serializer/deserializer can be configured using Kafka propertie - `JsonSerializer.ADD_TYPE_INFO_HEADERS` (default `true`); set to `false` to disable this feature on the `JsonSerializer` (sets the `addTypeInfo` property). - `JsonSerializer.TYPE_MAPPINGS` (default `empty`); see below. +- `JsonDeserializer.REMOVE_TYPE_INFO_HEADERS` (default `true`); set to `false` to retain headers set by the serializer. - `JsonDeserializer.KEY_DEFAULT_TYPE`; fallback type for deserialization of keys if no header information is present. - `JsonDeserializer.VALUE_DEFAULT_TYPE`; fallback type for deserialization of values if no header information is present. - `JsonDeserializer.TRUSTED_PACKAGES` (default `java.util`, `java.lang`); comma-delimited list of package patterns allowed for deserialization; `*` means deserialize all. - `JsonDeserializer.TYPE_MAPPINGS` (default `empty`); see below. +Starting with version 2.2, the type information headers (if added by the serializer) will be removed by the deserializer. +You can revert to the previous behavior by setting the `removeTypeHeaders` property to false, either directly on the deserializer, or with the configuration property described above. + **Mapping Types** Starting with version 2.2, you can now provide type mappings using the properties in the above list; previously you had to customize the type mapper within the serializer, deserializer. diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 6fbbef6e..0d3ab5e4 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -70,6 +70,8 @@ You can now provide type mapping information using producer/consumer properties. New constructors are available on the deserializer to allow overriding the type header information with the supplied target type. +The JsonDeserializer will now remove any type information headers by default. + See <> for more information. ==== Kafka Streams Changes