diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java index f25f99a654..3df6f79fce 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java @@ -678,7 +678,15 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context // hack to avoid clobbering properties with get/is names // it's ugly but gets around https://github.com/swagger-api/swagger-core/issues/415 - if (propDef.getPrimaryMember() != null) { + // + // This restores the raw member name for members that merely start with a get/is prefix + // (e.g. a Scala accessor "is_persistent", or a JAXB-renamed field, see #415 and #2635). + // It must NOT run when a custom PropertyNamingStrategy (e.g. SNAKE_CASE) is configured: + // in that case the strategy legitimately translates names such as "issuanceDate" -> + // "issuance_date" and the raw member name must not clobber the translated one + // (see springdoc/springdoc-openapi#3293). + if (propDef.getPrimaryMember() != null + && _mapper.getSerializationConfig().getPropertyNamingStrategy() == null) { final JsonProperty jsonPropertyAnn = propDef.getPrimaryMember().getAnnotation(JsonProperty.class); if (jsonPropertyAnn == null || !jsonPropertyAnn.value().equals(propName)) { if (member != null) { diff --git a/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/RecordPropertyNamingStrategyTest.java b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/RecordPropertyNamingStrategyTest.java new file mode 100644 index 0000000000..5596beb428 --- /dev/null +++ b/modules/swagger-java17-support/src/test/java/io/swagger/v3/java17/resolving/RecordPropertyNamingStrategyTest.java @@ -0,0 +1,52 @@ +package io.swagger.v3.java17.resolving; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverterContextImpl; +import io.swagger.v3.core.jackson.ModelResolver; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.models.media.Schema; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * Reproduces the property-naming inconsistency reported in + * springdoc/springdoc-openapi#3293: when a {@link PropertyNamingStrategies} such as + * {@code SNAKE_CASE} is configured, record components whose names start with the + * {@code is}/{@code get} accessor prefixes followed by a lower-case letter (e.g. + * {@code issuanceDate}) were not being translated, because the + * "avoid clobbering get/is names" hack in {@code ModelResolver} replaced the + * Jackson-resolved (translated) name with the raw component name. + */ +public class RecordPropertyNamingStrategyTest { + + public record PidLookupResponse( + String familyName, + String expiryDate, + String issuanceDate, + String issuingCountry, + String issuingAuthority + ) { + } + + @Test + public void testSnakeCaseNamingStrategyAppliedToRecordComponents() { + ModelResolver modelResolver = new ModelResolver( + Json.mapper().copy().setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)); + ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver); + + Schema schema = modelResolver.resolve(new AnnotatedType(PidLookupResponse.class), context, null); + + assertTrue(schema.getProperties().containsKey("family_name")); + assertTrue(schema.getProperties().containsKey("expiry_date")); + assertTrue(schema.getProperties().containsKey("issuance_date"), + "expected issuance_date but got " + schema.getProperties().keySet()); + assertTrue(schema.getProperties().containsKey("issuing_country"), + "expected issuing_country but got " + schema.getProperties().keySet()); + assertTrue(schema.getProperties().containsKey("issuing_authority"), + "expected issuing_authority but got " + schema.getProperties().keySet()); + assertEquals(schema.getProperties().size(), 5, "unexpected properties: " + schema.getProperties().keySet()); + } +}