-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DefaultAccessorNamingStrategy converts to lowercases the whole first group of uppercase characters #4682
Comments
The reason is abbreviations, e.g. HTMLParser -> htmlParser. That this doesn't work well with lombok is a known issue. |
hello @yawkat, thanks for the quick response!
|
Hmmmm, very tricky topic here. |
Maybe USE_STD_BEAN_NAMING is enough to fix your particular case. |
Unfortunately Tested here: @Test
void lowercasingMoreThanOneChar() throws Exception {
ObjectMapper mapper = new ObjectMapper().configure(USE_STD_BEAN_NAMING, true);
String json = mapper.writeValueAsString(new MyObj("test"));
assertEquals("{\"iPhone\":\"test\"}", json); // <-- fails, actual: {"IPhone":"test"}
} To summarize, switching between Lombok data/value classes (or just class with IDE-generated getters) to Java records results in different json for these cases. Maybe a new naming strategy or some config property can address this at a project level - but you'll know better if a new config property will make sense here. |
New naming strategy might be possible. But I doubt changing the default will push through --this will create disaster for so many projects out there.
With such expected side effect in mind, intuition may not enough to drive changing default behavior. |
@JooHyukKim - I never suggested changing the default configuration :) Just wanted to highlight this situation and ask if it is/will be possible to achieve this via a dedicated configuration at the project level. |
As I recall, the fundamental problem in case like this is that the name should be based on field name, and not on extracting it from getter or setter: latter is ambiguous as there's no 1-to-1 mapping. I don't think naming strategy is, unfortunately, quite enough to solve the problem. |
Right, I guess I thought that's where discussion was heading towards :)) |
EDIT: Nope, likely means
...means field named (I've just understood what that statement meant, just sharing in case it helps someone else.) NOTE: What happens internally is that
...then removing the 1st property because it is "not visible" (default settings only considers public field as visible), leaving us with the 2nd property. |
Records do not follow Bean convention anyway, due to no set-/get-prefixes. And no need to modify capitalization. |
Out of curiosity, I tried to hack something out but these things don't look like public APIs...: objectMapper.setAccessorNaming(new DefaultAccessorNamingStrategy.Provider() {
@Override
public AccessorNamingStrategy forPOJO(MapperConfig<?> config, AnnotatedClass targetClass) {
return new DefaultAccessorNamingStrategy(config, targetClass, _setterPrefix, _getterPrefix, _isGetterPrefix, _baseNameValidator) {
@Override
public String findNameForRegularGetter(AnnotatedMethod am, String methodName) {
if (_getterPrefix != null && methodName.startsWith(_getterPrefix)) {
String name = methodName.substring(_getterPrefix.length());
char firstChar = name.charAt(0);
char secondChar = name.charAt(1);
boolean annoyingName = Character.isUpperCase(firstChar) && Character.isUpperCase(secondChar);
if (annoyingName) {
int firstLowerCaseIndex = 0;
char[] chars = name.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (Character.isLowerCase(chars[i])) {
firstLowerCaseIndex = i;
break;
}
}
// To convert e.g.:
// - `IPhone` --> `iPhone`
// - `HTMLParser` --> `htmlParser`
int lastFrontUpperCaseIndex = firstLowerCaseIndex - 1;
return name.substring(0, lastFrontUpperCaseIndex).toLowerCase(Locale.ROOT)
+ name.substring(lastFrontUpperCaseIndex);
}
return super.findNameForRegularGetter(am, methodName);
}
return null;
}
};
}
}); Is this really the "official" way to provide an alternative |
'setAccessorNaming()' method seems quite public (behavior wise) to me. It provides 'Annotated' and value as inputs. 🤔 Also There might be some JavaDoc written about customizing idk |
While having |
So, So it is not an internal extension point. |
Hey @cowtowncoder and everyone else, thank you all for stepping in here! To clarify, I mentioned Lombok and records just to provide context for how we encountered the issue. The question is whether it’s ok to lowercase the entire first group of uppercase characters following the get/set prefix, without offering an alternative strategy via configuration. From what I can see, there are cases where this default behavior might make sense, particularly when dealing with single words in all uppercase:
However, this will make it impossible to serialize correctly -via getters- fields starting with a single lowercase character, such as: iPhone, iPad, eGaming, eCommerce... etc. So, I think the core question of this issue is: does it make sense to have a naming strategy that only lowercase the first letter following the get prefix? If not, and we should just use field serialization for these cases, I think we can go ahead and close this issue. :) |
I don't think that changing default behavior (either with or without I thought USE_STD_BEAN_NAMING would lower-case all leading upper-case; and non-USE_STD_BEAN_NAMING just the first one, fwtw. But ultimately to fix the issue would, I think, require new option: anchoring property name to Field name, and matching possibly case-mismatching setter/getter to that name. |
Oh. And looking at
so it won't do what would work here, |
Search before asking
Describe the bug
Hello, I've noticed that DefaultAccessorNamingStrategy will convert to lowercase more than one letter that follows the prefix of a getter. This is happening in the for-loop here: DefaultAccessorNamingStrategy::legacyManglePropertyName.
Based on the code and comments, this seems to be the intended default behavior, but it can result in unintuitive outcomes.
This was my use case:
If we use the default configuration, the resulting json can be a bit surprising:
Are there use cases where we can benefit from lowercasing more than one character?
Version Information
latest master version
Reproduction
Expected behavior
The expected outcome was to lowercase only the first character that follows the 'get' prefix.
Additional context
No response
The text was updated successfully, but these errors were encountered: