Why does Jackson 2 not recognize the first capital letter if the leading camel case word is only a single letter long?
The problem you are seeing is due to the fact that Jackson uses Java Bean naming conventions to figure out the the Json properties in a Java class.
Here is a reference of the specific problem you see, the recommendation is not to capitalize either of the first two letters in your field. If you use an IDE like IntelliJ or eclipse and let the IDE generate the setters for you, you will notice the same "behavior" occurs, you will end up with the following methods:
public void setaLogId(String aLogId) {
this.aLogId = aLogId;
}
public String getaLogId() {
return aLogId;
}
Hence, when you change the "L" to lower case Jackson was able to figure it out the field you wanted to map.
Having said the above, you still have the alternative to use the "aLogId" field name and make Jackson work all you have to do is use the @JsonProperty
annotation with the aLogId
in it.
@JsonProperty("aLogId")
private String aLogId;
The following test code is to show how this will work:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Test {
@JsonProperty("aLogId")
private String aLogId;
public void setaLogId(String aLogId) {
this.aLogId = aLogId;
}
public String getaLogId() {
return aLogId;
}
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Test test = new Test();
test.setaLogId("anId");
try {
System.out.println("Serialization test: " + objectMapper.writeValueAsString(test));
String json = "{\"aLogId\":\"anotherId\"}";
Test anotherTest = objectMapper.readValue(json, Test.class);
System.out.println("Deserialization test: " +anotherTest.getaLogId());
} catch (Exception e) {
e.printStackTrace();
}
}
}
The output of the test is:
Serialization test: {"aLogId":"anId"}
Deserialization test: anotherId
@JsonProperty
as suggested by the current answer has the disadvantage that you need to repeat it for every single property, and that it's invasive (you need to change the class being mapped).
A more general approach is to provide a custom Property Naming Strategy:
Java:
public class CustomSnakeCase extends PropertyNamingStrategy.PropertyNamingStrategyBase {
private static final Pattern REGEX = Pattern.compile("[A-Z]");
@Override
public String translate(String input) {
if (input == null)
return input; // garbage in, garbage out
if (!input.isEmpty() && Character.isUpperCase(input.charAt(0)))
input = input.substring(0, 1).toLowerCase() + input.substring(1);
return REGEX.matcher(input).replaceAll("_$0").toLowerCase();
}
}
Kotlin:
class CustomSnakeCase : PropertyNamingStrategy.PropertyNamingStrategyBase() {
private companion object {
val REGEX = Regex("[A-Z]")
}
override fun translate(input: String?) =
input?.decapitalize()?.replace(REGEX, "_$0")?.toLowerCase()
}
Usage:
new ObjectMapper()
.setPropertyNamingStrategy(new CustomSnakeCase())
.enable(MapperFeature.USE_STD_BEAN_NAMING)
Note:
The implementation I provide above assumes that input is camelCase
(no uppercase beginning). USE_STD_BEAN_NAMING
is needed to handle 1-character prefixes such as aField
consistely.
The implementation provides the following mapping, you might adjust it according to your needs:
camelCase snake_case
----------------------------
simple simple
a a
sepaRated sepa_rated
iOException i_o_exception
xOffset x_offset
theWWW the_w_w_w
sepaRated32 sepa_rated32
sepa32Rated sepa32_rated
This has worked for me; @JsonProperty annotation on getters!
import com.fasterxml.jackson.annotation.JsonProperty;
public class PaytmRequestJson {
private String ORDERID;
private String MID;
private String CHECKSUMHASH;
@JsonProperty("ORDERID")
public String getORDERID() {
return ORDERID;
}
public void setORDERID(String ORDERID) {
this.ORDERID = ORDERID;
}
@JsonProperty("MID")
public String getMID() {
return MID;
}
public void setMID(String MID) {
this.MID = MID;
}
@JsonProperty("CHECKSUMHASH")
public String getCHECKSUMHASH() {
return CHECKSUMHASH;
}
public void setCHECKSUMHASH(String CHECKSUMHASH) {
this.CHECKSUMHASH = CHECKSUMHASH;
}
}