From cc3b2d4edc750b42b4d28ef508b3c774e78bf863 Mon Sep 17 00:00:00 2001 From: frizz19 Date: Sun, 14 Jun 2026 02:11:35 +0800 Subject: [PATCH 1/3] fix: global naming strategy not applied to Java records (#7656) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a record's field name was resolved, `method.getName()` was called directly instead of `BeanUtils.getterName(…, namingStrategy)`, causing the global (and @JSONType) naming strategy to be silently ignored. Co-Authored-By: Claude Sonnet 4.6 --- .../fastjson2/writer/ObjectWriterCreator.java | 7 ++--- .../annotation/JSONTypeNamingSnake.java | 28 +++++++++++++++++++ .../issue3601/FastJson2Reader.java | 1 - .../issues_3600/issue3601/Issue3601.java | 1 - .../issues_3600/issue3601/MyArrayList.java | 2 -- 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java index 882bf38542..e0eb333f46 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java @@ -753,11 +753,8 @@ protected static String getFieldName( ) { String fieldName; if (fieldInfo.fieldName == null || fieldInfo.fieldName.isEmpty()) { - if (record) { - fieldName = method.getName(); - } else { - fieldName = BeanUtils.getterName(method, beanInfo.kotlin, beanInfo.namingStrategy); - + fieldName = BeanUtils.getterName(method, beanInfo.kotlin, beanInfo.namingStrategy); + if (!record) { Field field; if ((provider.userDefineMask & NAME_COMPATIBLE_WITH_FILED) != 0 && (field = BeanUtils.getField(objectClass, method)) != null) { diff --git a/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java b/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java index b3b599cea8..4330b482db 100644 --- a/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java +++ b/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java @@ -1,6 +1,7 @@ package com.alibaba.fastjson2.annotation; import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONFactory; import com.alibaba.fastjson2.PropertyNamingStrategy; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -52,4 +53,31 @@ public void setBeanName(String beanName) { this.beanName = beanName; } } + + @JSONType(naming = PropertyNamingStrategy.SnakeCase) + public record RecordDto(String currencyCode) {} + + @JSONType(naming = PropertyNamingStrategy.SnakeCase) + static class ClassDto { + public String currencyCode = "840"; + } + + @Test + public void testRecord() { + assertEquals("{\"currency_code\":\"840\"}", JSON.toJSONString(new RecordDto("840"))); + assertEquals("{\"currency_code\":\"840\"}", JSON.toJSONString(new ClassDto())); + } + + @Test + public void testRecordGlobalNaming() { + var provider = JSONFactory.getDefaultObjectWriterProvider(); + var prev = provider.getNamingStrategy(); + try { + provider.setNamingStrategy(PropertyNamingStrategy.SnakeCase); + record GlobalRecordDto(String currencyCode) {} + assertEquals("{\"currency_code\":\"840\"}", JSON.toJSONString(new GlobalRecordDto("840"))); + } finally { + provider.setNamingStrategy(prev); + } + } } diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/FastJson2Reader.java b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/FastJson2Reader.java index 6049ed3410..27d0071776 100644 --- a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/FastJson2Reader.java +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/FastJson2Reader.java @@ -4,7 +4,6 @@ import com.alibaba.fastjson2.modules.ObjectReaderModule; import com.alibaba.fastjson2.reader.ObjectReader; import com.alibaba.fastjson2.reader.ObjectReaderImplList; -import lombok.var; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/Issue3601.java b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/Issue3601.java index 7fc91455d8..abea3dacfb 100644 --- a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/Issue3601.java +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/Issue3601.java @@ -1,7 +1,6 @@ package com.alibaba.fastjson2.issues_3600.issue3601; import com.alibaba.fastjson2.JSON; -import lombok.var; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/MyArrayList.java b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/MyArrayList.java index 238c1116ae..6a83d5e20a 100644 --- a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/MyArrayList.java +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/MyArrayList.java @@ -1,7 +1,5 @@ package com.alibaba.fastjson2.issues_3600.issue3601; -import lombok.var; - import java.util.*; public class MyArrayList From dc90fd47a2aee35883edd5984ecacaceb9c2cd66 Mon Sep 17 00:00:00 2001 From: frizz19 Date: Wed, 17 Jun 2026 11:49:31 +0800 Subject: [PATCH 2/3] fix: address review comments for record naming strategy PR - Writer: use BeanUtils.fieldName() for records to avoid stripping is/get prefixes that are part of the field name in Java records - Reader: apply namingStrategy to record parameterNames so deserialized JSON keys (e.g. currency_code) match the strategy-applied names - Tests: move record tests to test-jdk17 module (Java 16+ required), replace local record and var with class-level record and explicit types, add round-trip deserialization assertions and boolean prefix test Co-Authored-By: Claude Sonnet 4.6 --- .../fastjson2/reader/ObjectReaderCreator.java | 5 +++ .../fastjson2/writer/ObjectWriterCreator.java | 6 ++- .../annotation/JSONTypeNamingSnake.java | 21 +--------- .../fastjson2/RecordNamingStrategyTest.java | 41 +++++++++++++++++++ 4 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 test-jdk17/src/test/java/com/alibaba/fastjson2/RecordNamingStrategyTest.java diff --git a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreator.java b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreator.java index 508d771b1c..dcaf2f5594 100644 --- a/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreator.java +++ b/core/src/main/java/com/alibaba/fastjson2/reader/ObjectReaderCreator.java @@ -1279,6 +1279,11 @@ boolean record = BeanUtils.isRecord(objectClass); String[] parameterNames = beanInfo.createParameterNames; if (record && parameterNames == null) { parameterNames = BeanUtils.getRecordFieldNames(objectClass); + if (beanInfo.namingStrategy != null && parameterNames != null) { + for (int i = 0; i < parameterNames.length; i++) { + parameterNames[i] = BeanUtils.fieldName(parameterNames[i], beanInfo.namingStrategy); + } + } } if (parameterNames == null || parameterNames.length == 0) { diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java index e0eb333f46..7f7f746e15 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java @@ -753,8 +753,10 @@ protected static String getFieldName( ) { String fieldName; if (fieldInfo.fieldName == null || fieldInfo.fieldName.isEmpty()) { - fieldName = BeanUtils.getterName(method, beanInfo.kotlin, beanInfo.namingStrategy); - if (!record) { + if (record) { + fieldName = BeanUtils.fieldName(method.getName(), beanInfo.namingStrategy); + } else { + fieldName = BeanUtils.getterName(method, beanInfo.kotlin, beanInfo.namingStrategy); Field field; if ((provider.userDefineMask & NAME_COMPATIBLE_WITH_FILED) != 0 && (field = BeanUtils.getField(objectClass, method)) != null) { diff --git a/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java b/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java index 4330b482db..f93dd926d6 100644 --- a/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java +++ b/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java @@ -1,8 +1,6 @@ package com.alibaba.fastjson2.annotation; import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONFactory; -import com.alibaba.fastjson2.PropertyNamingStrategy; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -54,30 +52,13 @@ public void setBeanName(String beanName) { } } - @JSONType(naming = PropertyNamingStrategy.SnakeCase) - public record RecordDto(String currencyCode) {} - @JSONType(naming = PropertyNamingStrategy.SnakeCase) static class ClassDto { public String currencyCode = "840"; } @Test - public void testRecord() { - assertEquals("{\"currency_code\":\"840\"}", JSON.toJSONString(new RecordDto("840"))); + public void testClass() { assertEquals("{\"currency_code\":\"840\"}", JSON.toJSONString(new ClassDto())); } - - @Test - public void testRecordGlobalNaming() { - var provider = JSONFactory.getDefaultObjectWriterProvider(); - var prev = provider.getNamingStrategy(); - try { - provider.setNamingStrategy(PropertyNamingStrategy.SnakeCase); - record GlobalRecordDto(String currencyCode) {} - assertEquals("{\"currency_code\":\"840\"}", JSON.toJSONString(new GlobalRecordDto("840"))); - } finally { - provider.setNamingStrategy(prev); - } - } } diff --git a/test-jdk17/src/test/java/com/alibaba/fastjson2/RecordNamingStrategyTest.java b/test-jdk17/src/test/java/com/alibaba/fastjson2/RecordNamingStrategyTest.java new file mode 100644 index 0000000000..2756af9059 --- /dev/null +++ b/test-jdk17/src/test/java/com/alibaba/fastjson2/RecordNamingStrategyTest.java @@ -0,0 +1,41 @@ +package com.alibaba.fastjson2; + +import com.alibaba.fastjson2.annotation.JSONType; +import com.alibaba.fastjson2.writer.ObjectWriterProvider; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class RecordNamingStrategyTest { + @JSONType(naming = PropertyNamingStrategy.SnakeCase) + public record RecordDto(String currencyCode) {} + + public record GlobalRecordDto(String currencyCode) {} + + @JSONType(naming = PropertyNamingStrategy.SnakeCase) + public record BooleanRecordDto(boolean isActive) {} + + @Test + public void testRecordAnnotationNaming() { + assertEquals("{\"currency_code\":\"840\"}", JSON.toJSONString(new RecordDto("840"))); + assertEquals(new RecordDto("840"), JSON.parseObject("{\"currency_code\":\"840\"}", RecordDto.class)); + } + + @Test + public void testBooleanFieldNotStripped() { + assertEquals("{\"is_active\":true}", JSON.toJSONString(new BooleanRecordDto(true))); + assertEquals(new BooleanRecordDto(true), JSON.parseObject("{\"is_active\":true}", BooleanRecordDto.class)); + } + + @Test + public void testRecordGlobalNaming() { + ObjectWriterProvider provider = JSONFactory.getDefaultObjectWriterProvider(); + PropertyNamingStrategy prev = provider.getNamingStrategy(); + try { + provider.setNamingStrategy(PropertyNamingStrategy.SnakeCase); + assertEquals("{\"currency_code\":\"840\"}", JSON.toJSONString(new GlobalRecordDto("840"))); + } finally { + provider.setNamingStrategy(prev); + } + } +} From db6666405a927bbcb3b983cfb31ff8fb6702c307 Mon Sep 17 00:00:00 2001 From: frizz19 Date: Tue, 23 Jun 2026 15:11:56 +0800 Subject: [PATCH 3/3] fix: address record naming review comments --- .../fastjson2/writer/ObjectWriterCreator.java | 4 +++- .../fastjson2/annotation/JSONTypeNamingSnake.java | 1 + .../issues_3600/issue3601/FastJson2Reader.java | 1 + .../fastjson2/issues_3600/issue3601/Issue3601.java | 1 + .../issues_3600/issue3601/MyArrayList.java | 2 ++ .../fastjson2/RecordNamingStrategyTest.java | 14 ++++++++++---- 6 files changed, 18 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java index 7f7f746e15..0a7adaf998 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreator.java @@ -754,7 +754,9 @@ protected static String getFieldName( String fieldName; if (fieldInfo.fieldName == null || fieldInfo.fieldName.isEmpty()) { if (record) { - fieldName = BeanUtils.fieldName(method.getName(), beanInfo.namingStrategy); + fieldName = beanInfo.namingStrategy != null + ? BeanUtils.fieldName(method.getName(), beanInfo.namingStrategy) + : method.getName(); } else { fieldName = BeanUtils.getterName(method, beanInfo.kotlin, beanInfo.namingStrategy); Field field; diff --git a/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java b/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java index f93dd926d6..75c15bc42d 100644 --- a/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java +++ b/core/src/test/java/com/alibaba/fastjson2/annotation/JSONTypeNamingSnake.java @@ -1,6 +1,7 @@ package com.alibaba.fastjson2.annotation; import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.PropertyNamingStrategy; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/FastJson2Reader.java b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/FastJson2Reader.java index 27d0071776..6049ed3410 100644 --- a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/FastJson2Reader.java +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/FastJson2Reader.java @@ -4,6 +4,7 @@ import com.alibaba.fastjson2.modules.ObjectReaderModule; import com.alibaba.fastjson2.reader.ObjectReader; import com.alibaba.fastjson2.reader.ObjectReaderImplList; +import lombok.var; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/Issue3601.java b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/Issue3601.java index abea3dacfb..7fc91455d8 100644 --- a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/Issue3601.java +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/Issue3601.java @@ -1,6 +1,7 @@ package com.alibaba.fastjson2.issues_3600.issue3601; import com.alibaba.fastjson2.JSON; +import lombok.var; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/MyArrayList.java b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/MyArrayList.java index 6a83d5e20a..238c1116ae 100644 --- a/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/MyArrayList.java +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3600/issue3601/MyArrayList.java @@ -1,5 +1,7 @@ package com.alibaba.fastjson2.issues_3600.issue3601; +import lombok.var; + import java.util.*; public class MyArrayList diff --git a/test-jdk17/src/test/java/com/alibaba/fastjson2/RecordNamingStrategyTest.java b/test-jdk17/src/test/java/com/alibaba/fastjson2/RecordNamingStrategyTest.java index 2756af9059..a9dd71dba5 100644 --- a/test-jdk17/src/test/java/com/alibaba/fastjson2/RecordNamingStrategyTest.java +++ b/test-jdk17/src/test/java/com/alibaba/fastjson2/RecordNamingStrategyTest.java @@ -1,6 +1,7 @@ package com.alibaba.fastjson2; import com.alibaba.fastjson2.annotation.JSONType; +import com.alibaba.fastjson2.reader.ObjectReaderProvider; import com.alibaba.fastjson2.writer.ObjectWriterProvider; import org.junit.jupiter.api.Test; @@ -29,13 +30,18 @@ public void testBooleanFieldNotStripped() { @Test public void testRecordGlobalNaming() { - ObjectWriterProvider provider = JSONFactory.getDefaultObjectWriterProvider(); - PropertyNamingStrategy prev = provider.getNamingStrategy(); + ObjectWriterProvider writerProvider = JSONFactory.getDefaultObjectWriterProvider(); + ObjectReaderProvider readerProvider = JSONFactory.getDefaultObjectReaderProvider(); + PropertyNamingStrategy prevWriter = writerProvider.getNamingStrategy(); + PropertyNamingStrategy prevReader = readerProvider.getNamingStrategy(); try { - provider.setNamingStrategy(PropertyNamingStrategy.SnakeCase); + writerProvider.setNamingStrategy(PropertyNamingStrategy.SnakeCase); + readerProvider.setNamingStrategy(PropertyNamingStrategy.SnakeCase); assertEquals("{\"currency_code\":\"840\"}", JSON.toJSONString(new GlobalRecordDto("840"))); + assertEquals(new GlobalRecordDto("840"), JSON.parseObject("{\"currency_code\":\"840\"}", GlobalRecordDto.class)); } finally { - provider.setNamingStrategy(prev); + writerProvider.setNamingStrategy(prevWriter); + readerProvider.setNamingStrategy(prevReader); } } }