Skip to content

Generics using wildcard not correctly resolved #5285

Description

@wimdeblauwe

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

I opened a Spring Boot issue, but it seems the issue is Jackson related. See spring-projects/spring-boot#46994 (comment) for the full details and a reproducer.

Version Information

2.19.2

Reproduction

package com.example.jsontesterbug;

import static org.assertj.core.api.Assertions.assertThat;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

class MessageWrapperTest {
	
  private MessageWrapper<?> wildcardWrapper;
  
  private MessageWrapper<EmailSettings> specificWrapper;

  private ObjectMapper objectMapper = new ObjectMapper();
  
  @Test
  void wildcardWrapper() throws NoSuchFieldException, SecurityException, JsonProcessingException {
	  serializeWithTypeFromField("wildcardWrapper");
  }
  
  @Test
  void specificWrapper() throws NoSuchFieldException, SecurityException, JsonProcessingException {
	  serializeWithTypeFromField("specificWrapper");
  }
  
  private void serializeWithTypeFromField(String field) throws NoSuchFieldException, SecurityException, JsonProcessingException {
	  MessageWrapper<EmailSettings> wrapper = new MessageWrapper<>(new EmailSettings("me@me.com"),
			  "Sample Message");
	  Type genericType = MessageWrapperTest.class.getDeclaredField(field).getGenericType();
	  TypeVariable<?>[] typeParameters = ((Class<?>)((ParameterizedType)genericType).getRawType()).getTypeParameters();
	  Type[] bounds = typeParameters[0].getBounds();
	  System.out.println(field);
	  System.out.println("  Generic type: " + genericType);
	  System.out.println("    Bounds: " + Arrays.toString(bounds));
	  JavaType jacksonType = this.objectMapper.constructType(genericType);
	  System.out.println("  Jackson type: " + jacksonType);
	  String json = this.objectMapper.writerFor(jacksonType).writeValueAsString(wrapper);
	  System.out.println("  JSON: " + json);
	  assertThat(json).contains("\"type\":\"EMAIL\"");
	  assertThat(json).contains("\"email\":\"me@me.com\"");
  }

}

Running this gives this output:

wildcardWrapper
  Generic type: com.example.jsontesterbug.MessageWrapper<?>
    Bounds: [interface com.example.jsontesterbug.Settings]
  Jackson type: [simple type, class com.example.jsontesterbug.MessageWrapper<java.lang.Object>]
  JSON: {"settings":{"email":"me@me.com"},"message":"Sample Message"}
specificWrapper
  Generic type: com.example.jsontesterbug.MessageWrapper<com.example.jsontesterbug.EmailSettings>
    Bounds: [interface com.example.jsontesterbug.Settings]
  Jackson type: [simple type, class com.example.jsontesterbug.MessageWrapper<com.example.jsontesterbug.EmailSettings>]
  JSON: {"settings":{"type":"EMAIL","email":"me@me.com"},"message":"Sample Message"}

Expected behavior

The JSON output should be the same in both cases.

Additional context

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions