2020import java .util .Arrays ;
2121import java .util .Collections ;
2222import java .util .Map ;
23+ import java .util .function .BiFunction ;
2324
2425import org .apache .kafka .common .errors .SerializationException ;
2526import org .apache .kafka .common .header .Headers ;
@@ -92,20 +93,32 @@ public class JsonDeserializer<T> implements Deserializer<T> {
9293 */
9394 public static final String USE_TYPE_INFO_HEADERS = "spring.json.use.type.headers" ;
9495
96+ /**
97+ * A method name to determine the {@link JavaType} to deserialize the key to.
98+ */
99+ public static final String KEY_TYPE_METHOD = "spring.json.key.type.method" ;
100+
101+ /**
102+ * A method name to determine the {@link JavaType} to deserialize the key to.
103+ */
104+ public static final String VALUE_TYPE_METHOD = "spring.json.value.type.method" ;
105+
95106 protected final ObjectMapper objectMapper ; // NOSONAR
96107
97108 protected JavaType targetType ; // NOSONAR
98109
99110 protected Jackson2JavaTypeMapper typeMapper = new DefaultJackson2JavaTypeMapper (); // NOSONAR
100111
101- private volatile ObjectReader reader ;
112+ private ObjectReader reader ;
102113
103114 private boolean typeMapperExplicitlySet = false ;
104115
105116 private boolean removeTypeHeaders = true ;
106117
107118 private boolean useTypeHeaders = true ;
108119
120+ private BiFunction <byte [], Headers , JavaType > typeFunction ;
121+
109122 /**
110123 * Construct an instance with a default {@link ObjectMapper}.
111124 */
@@ -242,19 +255,6 @@ public JsonDeserializer(@Nullable JavaType targetType, ObjectMapper objectMapper
242255 initialize (targetType , useHeadersIfPresent );
243256 }
244257
245- private void initialize (@ Nullable JavaType type , boolean useHeadersIfPresent ) {
246- this .targetType = type ;
247- Assert .isTrue (this .targetType != null || useHeadersIfPresent ,
248- "'targetType' cannot be null if 'useHeadersIfPresent' is false" );
249-
250- if (this .targetType != null ) {
251- this .reader = this .objectMapper .readerFor (this .targetType );
252- }
253-
254- addTargetPackageToTrusted ();
255- this .typeMapper .setTypePrecedence (useHeadersIfPresent ? TypePrecedence .TYPE_ID : TypePrecedence .INFERRED );
256- }
257-
258258 public Jackson2JavaTypeMapper getTypeMapper () {
259259 return this .typeMapper ;
260260 }
@@ -307,6 +307,16 @@ public void setUseTypeHeaders(boolean useTypeHeaders) {
307307 }
308308 }
309309
310+ /**
311+ * Set a {@link BiFunction} that receives the data to be deserialized and the headers
312+ * and returns a JavaType.
313+ * @param typeFunction the function.
314+ * @since 2.5
315+ */
316+ public void setTypeFunction (BiFunction <byte [], Headers , JavaType > typeFunction ) {
317+ this .typeFunction = typeFunction ;
318+ }
319+
310320 @ Override
311321 public void configure (Map <String , ?> configs , boolean isKey ) {
312322 setUseTypeMapperForKey (isKey );
@@ -325,6 +335,17 @@ public void configure(Map<String, ?> configs, boolean isKey) {
325335 if (configs .containsKey (REMOVE_TYPE_INFO_HEADERS )) {
326336 this .removeTypeHeaders = Boolean .parseBoolean (configs .get (REMOVE_TYPE_INFO_HEADERS ).toString ());
327337 }
338+ if (isKey && configs .containsKey (KEY_TYPE_METHOD )) {
339+ setUpTypeFuntion ((String ) configs .get (KEY_TYPE_METHOD ));
340+ }
341+ else if (!isKey && configs .containsKey (VALUE_TYPE_METHOD )) {
342+ setUpTypeFuntion ((String ) configs .get (VALUE_TYPE_METHOD ));
343+ }
344+ }
345+
346+ private void setUpTypeFuntion (String method ) {
347+ this .typeFunction = SerializationUtils .propertyToMethodInvokingFunction (method , byte [].class ,
348+ getClass ().getClassLoader ());
328349 }
329350
330351 private void setUpTypePrecedence (Map <String , ?> configs ) {
@@ -355,6 +376,19 @@ else if (!isKey && configs.containsKey(VALUE_DEFAULT_TYPE)) {
355376 }
356377 }
357378
379+ private void initialize (@ Nullable JavaType type , boolean useHeadersIfPresent ) {
380+ this .targetType = type ;
381+ Assert .isTrue (this .targetType != null || useHeadersIfPresent ,
382+ "'targetType' cannot be null if 'useHeadersIfPresent' is false" );
383+
384+ if (this .targetType != null ) {
385+ this .reader = this .objectMapper .readerFor (this .targetType );
386+ }
387+
388+ addTargetPackageToTrusted ();
389+ this .typeMapper .setTypePrecedence (useHeadersIfPresent ? TypePrecedence .TYPE_ID : TypePrecedence .INFERRED );
390+ }
391+
358392 private JavaType setupTargetType (Map <String , ?> configs , String key ) throws ClassNotFoundException , LinkageError {
359393 if (configs .get (key ) instanceof Class ) {
360394 return TypeFactory .defaultInstance ().constructType ((Class <?>) configs .get (key ));
@@ -401,11 +435,15 @@ public T deserialize(String topic, Headers headers, byte[] data) {
401435 return null ;
402436 }
403437 ObjectReader deserReader = null ;
404- if (this .typeMapper .getTypePrecedence ().equals (TypePrecedence .TYPE_ID )) {
405- JavaType javaType = this .typeMapper .toJavaType (headers );
406- if (javaType != null ) {
407- deserReader = this .objectMapper .readerFor (javaType );
408- }
438+ JavaType javaType = null ;
439+ if (this .typeFunction != null ) {
440+ javaType = this .typeFunction .apply (data , headers );
441+ }
442+ if (javaType == null && this .typeMapper .getTypePrecedence ().equals (TypePrecedence .TYPE_ID )) {
443+ javaType = this .typeMapper .toJavaType (headers );
444+ }
445+ if (javaType != null ) {
446+ deserReader = this .objectMapper .readerFor (javaType );
409447 }
410448 if (this .removeTypeHeaders ) {
411449 this .typeMapper .removeHeaders (headers );
@@ -428,9 +466,16 @@ public T deserialize(String topic, @Nullable byte[] data) {
428466 if (data == null ) {
429467 return null ;
430468 }
431- Assert .state (this .reader != null , "No headers available and no default type provided" );
469+ ObjectReader localReader = this .reader ;
470+ if (this .typeFunction != null ) {
471+ JavaType javaType = this .typeFunction .apply (data , null );
472+ if (javaType != null ) {
473+ localReader = this .objectMapper .readerFor (javaType );
474+ }
475+ }
476+ Assert .state (localReader != null , "No headers available and no default type provided" );
432477 try {
433- return this . reader .readValue (data );
478+ return localReader .readValue (data );
434479 }
435480 catch (IOException e ) {
436481 throw new SerializationException ("Can't deserialize data [" + Arrays .toString (data ) +
@@ -490,4 +535,28 @@ public JsonDeserializer<T> typeMapper(Jackson2JavaTypeMapper mapper) {
490535 return this ;
491536 }
492537
538+ /**
539+ * Add trusted packages to the default type mapper.
540+ * @param packages the packages.
541+ * @return the deserializer.
542+ * @since 2,5
543+ */
544+ public JsonDeserializer <T > trustedPackages (String ... packages ) {
545+ Assert .isTrue (!this .typeMapperExplicitlySet , "When using a custom type mapper, set the trusted packages there" );
546+ this .typeMapper .addTrustedPackages (packages );
547+ return this ;
548+ }
549+
550+ /**
551+ * Set a {@link BiFunction} that receives the data to be deserialized and the headers
552+ * and returns a JavaType.
553+ * @param typeFunction the function.
554+ * @return the deserializer.
555+ * @since 2.5
556+ */
557+ public JsonDeserializer <T > typeFunction (BiFunction <byte [], Headers , JavaType > typeFunction ) {
558+ setTypeFunction (typeFunction );
559+ return this ;
560+ }
561+
493562}
0 commit comments