Skip to content

Commit

Permalink
fix: resolve bean field/method types earlier to unwrap generic types
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Edgar <[email protected]>
  • Loading branch information
MikeEdgar committed Dec 9, 2024
1 parent 875dc7a commit b2bc71b
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,17 @@ private boolean isExposedByDefault() {
* @return resolved type (if found)
*/
private Type getResolvedType(Type fieldType) {
return getResolvedType(fieldType, resolutionStack);
}

/**
* Resolve a type against the given resolution stack
*
* @param fieldType type to resolve
* @param resolutionStack stack of types for resolution
* @return resolved type (if found)
*/
private static Type getResolvedType(Type fieldType, Deque<Map<String, Type>> resolutionStack) {
Type current = TypeUtil.resolveWildcard(fieldType);

for (Map<String, Type> map : resolutionStack) {
Expand Down Expand Up @@ -571,7 +582,7 @@ private static List<MethodInfo> methods(AnnotationScannerContext context, ClassI
if (context.getConfig().sortedPropertiesEnable()) {
return currentClass.methods();
}
return currentClass.unsortedMethods();
return currentClass.methodsInDeclarationOrder();
}

private static boolean acceptMethod(MethodInfo method) {
Expand Down Expand Up @@ -785,7 +796,7 @@ private static void scanField(AnnotationScannerContext context, Map<String, Type
AnnotationTarget reference,
List<ClassInfo> descendants) {
final String propertyName = field.name();
final Type fieldType = field.type();
final Type fieldType = getResolvedType(field.type(), stack);
final ClassInfo fieldClass = context.getAugmentedIndex().getClass(fieldType);
final boolean unwrapped;
final TypeResolver resolver;
Expand Down Expand Up @@ -878,7 +889,7 @@ private static void scanMethod(AnnotationScannerContext context, Map<String, Typ
Deque<Map<String, Type>> stack,
AnnotationTarget reference,
List<ClassInfo> descendants) {
Type returnType = method.returnType();
Type returnType = getResolvedType(method.returnType(), stack);
Type propertyType = null;

if (isAccessor(context, method)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.time.Instant;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
Expand Down Expand Up @@ -331,43 +332,73 @@ void testJakartaJaxbElementUnwrapped() throws IOException, JSONException {
@Test
void testJacksonJsonUnwrapped() throws IOException, JSONException {
assertJsonEquals("components.schemas-jackson-jsonunwrapped.json",
JacksonJsonPerson.class, JacksonJsonPersonWithPrefixedAddress.class,
JacksonJsonPersonWithSuffixedAddress.class, JacksonJsonAddress.class);
JacksonJsonUnwrapped.JacksonJsonPerson.class,
JacksonJsonUnwrapped.JacksonJsonPersonWithPrefixedAddress.class,
JacksonJsonUnwrapped.JacksonJsonPersonWithSuffixedAddress.class,
JacksonJsonUnwrapped.JacksonJsonAddress.class,
JacksonJsonUnwrapped.TimestampedEntity.class,
JacksonJsonUnwrapped.Alternative.class,
JacksonJsonUnwrapped.Greeting.class,
JacksonJsonUnwrapped.LanguageAlternatives.class);
}

@Schema
static class JacksonJsonPerson {
protected String name;
@JsonUnwrapped
protected JacksonJsonAddress address;
static class JacksonJsonUnwrapped {
@Schema
static class JacksonJsonPerson {
protected String name;
@JsonUnwrapped
protected JacksonJsonAddress address;
protected TimestampedEntity<Greeting<LanguageAlternatives>> greeting;

@Schema(description = "Ignored since address is unwrapped")
public JacksonJsonAddress getAddress() {
return address;
@Schema(description = "Ignored since address is unwrapped")
public JacksonJsonAddress getAddress() {
return address;
}
}
}

@Schema
static class JacksonJsonPersonWithPrefixedAddress {
protected String name;
@JsonUnwrapped(prefix = "addr-")
protected JacksonJsonAddress address;
}
@Schema
static class JacksonJsonPersonWithPrefixedAddress {
protected String name;
@JsonUnwrapped(prefix = "addr-")
protected JacksonJsonAddress address;
}

@Schema
static class JacksonJsonPersonWithSuffixedAddress {
protected String name;
@JsonUnwrapped(suffix = "-addr")
protected JacksonJsonAddress address;
}
@Schema
static class JacksonJsonPersonWithSuffixedAddress {
protected String name;
@JsonUnwrapped(suffix = "-addr")
protected JacksonJsonAddress address;
}

@Schema
static class JacksonJsonAddress {
protected int streetNumber;
protected String streetName;
protected String city;
protected String state;
protected String postalCode;
@Schema
static class JacksonJsonAddress {
protected int streetNumber;
protected String streetName;
protected String city;
protected String state;
protected String postalCode;
}

static class TimestampedEntity<T> {
@JsonUnwrapped
T entity;
Instant timestamp;
}

interface Alternative {
}

static class Greeting<T extends Alternative> {
String message;
T alternatives;
}

static class LanguageAlternatives implements Alternative {
@Schema(required = true, example = "Hola")
String spanish;
@Schema(required = true, example = "Hallo")
String german;
}
}

/****************************************************************/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"type": "string"
},
"streetNumber": {
"format": "int32",
"type": "integer"
"type": "integer",
"format": "int32"
},
"city": {
"type": "string"
Expand All @@ -23,6 +23,9 @@
},
"name": {
"type": "string"
},
"greeting": {
"$ref": "#/components/schemas/TimestampedEntityGreetingLanguageAlternatives"
}
}
},
Expand All @@ -42,8 +45,8 @@
"type": "string"
},
"addr-streetNumber": {
"format": "int32",
"type": "integer"
"type": "integer",
"format": "int32"
},
"name": {
"type": "string"
Expand All @@ -63,8 +66,8 @@
"type": "string"
},
"streetNumber-addr": {
"format": "int32",
"type": "integer"
"type": "integer",
"format": "int32"
},
"state-addr": {
"type": "string"
Expand All @@ -77,21 +80,60 @@
"JacksonJsonAddress": {
"type": "object",
"properties": {
"city": {
"streetNumber": {
"type": "integer",
"format": "int32"
},
"streetName": {
"type": "string"
},
"postalCode": {
"city": {
"type": "string"
},
"state": {
"type": "string"
},
"streetName": {
"postalCode": {
"type": "string"
}
}
},
"LanguageAlternatives": {
"type": "object",
"required": [
"spanish",
"german"
],
"properties": {
"spanish": {
"type": "string",
"examples": [
"Hola"
]
},
"german": {
"type": "string",
"examples": [
"Hallo"
]
}
}
},
"TimestampedEntityGreetingLanguageAlternatives": {
"type": "object",
"properties": {
"alternatives": {
"$ref": "#/components/schemas/LanguageAlternatives"
},
"message": {
"type": "string"
},
"streetNumber": {
"format": "int32",
"type": "integer"
"timestamp": {
"type": "string",
"format": "date-time",
"examples": [
"2022-03-10T16:15:50Z"
]
}
}
}
Expand Down

0 comments on commit b2bc71b

Please sign in to comment.