Jackson naming convention issue in serialization with uncommon getter method name - java

New to java and spring boot.
While trying to serialize the following class,
public class ActionItems {
private String APpID;
public String getAPpID() {
return APpID;
}
public void setAPpID(String aPpID) {
APpID = aPpID;
}
// other fields
}
got the json string as
{
"appID": null,
}
Whilst, cross checking the getter name with decapitilize(), it is matching with the field name.
Introspector.decapitalize("APpID") - gives "APpID"
Is jackson using a different set of rules and methods when generating the property name from the getter method?
PS: I am aware that, variable name should begin with small case. While going through the java beans naming convention spec got this question.
I am using jackson 2.9.3v.
PS: As per the link PropertyNamingStrategy, it should have produced APpID instead of appId right?
Could someone provide some input here?
Thanks.

In Jackson, you can custom PropertyNamingStrategy, and
In absence of a registered custom strategy, default Java property
naming strategy is used, which leaves field names as is, and removes
set/get/is prefix from methods (as well as lower-cases initial
sequence of capitalized characters).
Also, you can custom a property name like:
#JsonProperty("APpID") // produce {"APpID":"s"}
public String getAPpID() {
return APpID;
}

Related

SpringMVC can not bind json correctly

There a json object to be sent to the server, which contains a field:
{"sName":"something"}
In my request model,I declare a var with the same name:
String sName;
But I got null when I receive in the Controller.
I change the field name to lower-case(sname) or add JsonProperty(value="sName") annotation,it work. So where is the problem?
Controller
public ResponseEntity<JSONObject> getComprehensiveInquiryCp(#Validated #RequestBody ComprehensiveInquiryRequestModel body) {
Map<String, Object> content;
JSONObject result = new JSONObject();
String sLicense = body.getSLicense();
...
}
ComprehensiveInquiryRequestModel
#Data
public class ComprehensiveInquiryRequestModel {
...
//#JsonProperty(value = "sLicense")
private String sLicense;
...
}
From top of my head: if you have accessors in that bean, then I think jackson prefers to use them if they exist. And/or Jackson prefers accessors for private fields. As you noticed you can alter that behaviour with Jakson configuration (for example via annotions).
Try:
1. to debug, remove accessor methods and make field public. If that works then change the field back to private and make sure accessor methods are named correctly.
Also single charater prefixes are not a good practise. They can be problematic and confusing. Prefixes in general are lazy and un-Clean Code(tm) practise.
It is important that your setters (and getters) are present and actually conform to the Java naming conventions. A json property named "myFirstName" usually requires a public setter "setMyFirstName(...)" for example. So "sName" needs "setSName()", I guess.
Sure sounds like the naming convnetion might be at fault here.

adding "is" prefix to a Boolean json request field

I'm using JAX-RS and Dropwizard to develop an API (I'm pretty new to both)
I want to have a json field called isInNetwork for example. But the strange thing for me is after defining the model and resource, I see inNetwork as the defined json field. (the is prefix is removed)
to be more specific when I define the model as:
#JsonProperty
private Boolean isInNetwork;
and when I run the server I see "inNetwork": false
wanted to know if that's something by default (that I'm not aware of) set by dropwizard / jax-rs for Booleans?
Don't use "is" prefix for the field at all, because of "is" prefix is used for getters for boolean fields. getIsInNetwork method name looks bad.
Better to specify JSON property name for the field
#JsonProperty("isInNetwork")
private Boolean inNetwork;
public Boolean isInNetwork() {
return inNetwork;
}
public void setInNetwork(Boolean inNetwork) {
return this.inNetwork = inNetwork;
}

Is there a better way of obtaining an object's field getters other than java reflection api or i am misusing PropertyDescriptor's getReadMethod?

Context:
I am building an Excel document in a generic way with data i receive from a SOAP service endpoint. I receive the data as a List and i have the model (JavaBeans) for every Object i receive according to the method called.
So I set the first row of the sheet as the header from the object's fields (getDeclaredFields).
Then i go on filling up the column row by row with values from the list of objects.
The problem:
I haven't found a workable way of getting the object's field values.
I have tried using the getters with the java reflection API with something like this answer's https://stackoverflow.com/a/5503534/4807777 findGetterName , findGetter however the PropertyDescriptor's getName sometimes is a different letter case from the field name as obtained from the class's getDeclaredFields.
Let's say i overcome this by capitalizing both names, the getReadMethod stil fails - doesn't seem to find getters for the fields which use the is prefix (i.e boolean fields). I don't know if i am misusing it or it is a bug (debugging the getReadMethod appears to only work with the get prefix, even though it appears to handle the is prefix case for booleans).
Considering the fact the fields aren't accesible outside of the object's package, therefore solely through invoking getters.
Is there a better way of obtaining the object's field getters or i am missing something with the getter methods?
Update: Spring's BeanUtils seems to be better for getting the properties with it's getPropertyDescriptors is better than java Class's getDeclaredFields, when the JavaBean properties are mapped to XML elements.
This fixes the different letter cases situation. However it stil doesn't find it's readMethod when not using the get prefix.
Edited - to show an example of getReadMethod not finding the is prefixed getter, as Laszlo Lugosi requested.
A simple class:
class Test {
private String assignmentType;
private Boolean conserved;
public String getAssignmentType() {return assignmentType;}
public void setAssignmentType(String assignmentType) {this.assignmentType = assignmentType;}
public Boolean isConserved() {return conserved;}
public void setConserved(Boolean conserved) {this.conserved = conserved;}
}
Run this with the findGetter and findGetterName written in the answer linked above:
{
Test obj = new Test();
obj.setAssignmentType("someType");
obj.setConserved(true);
Field[] fields = obj.getClass().getDeclaredFields();
String fieldName;
for (int i=0;i<fields.length;i++){
fieldName = fields[i].getName();
java.lang.reflect.Method method;
Object val = null;
try {
method = obj.getClass().getMethod(findGetterName(obj.getClass(),fieldName));
val = method.invoke(obj);
}
catch (Exception e){
e.printStackTrace();
}
}
}
Edited 2
While i could simply write a getReadMethod following the convention Laszlo Lugosi highlighted i do prefer finding an API for handling accessors.
As you know only the object field name, and JavaBean has convention, you can figure out the getters easily. The rules are getUpperfieldname() and isUpperfieldname if field is boolean. And you can find out the return type as well from the object field.

Can I name my variable 'public' in Java for JSON serialization/deserialization?

So I'm working with a public API that sends me a JSON object with a boolean field named 'public'.
{
...
"public" : true,
...
}
I'm using GSON to parse this into a Java class, but this forces me to declare the following variable, which of course the Java lexer/parser does not agree with:
private boolean public;
Is there any way to allow my variable to be named public?
Use Gson's #SerializedName annotation.
#SerializedName("public")
private boolean someOtherName;
You can't use the word public (or any of the other reserved keywords) as an identifier name in Java source code.
Most feature-rich JSON parsers will have such an annotation or other mechanism for naming JSON keys, which don't have as many naming restrictions.

PersistenceConstructor argument variable name doesn't match instance variable name

I'm trying to persist the following object with spring-data-mongodb version 1.1.1.RELEASE:
#Document
public static class TestObject {
private final int m_property;
#PersistenceConstructor
public TestObject(int a_property) {
m_property = a_property;
}
public int property() {
return m_property;
}
}
I get a MappingException when I try to read the object back from the database (see full stacktrace below)
The naming convention my group uses requires argument variable names to be prefaced by a_ and instance variable names to be prefaced by m_. It seems like spring-data-mongodb is making the assumption that the constructor argument variable names must match the object instance variable names.
Why doesn't spring-data-mongodb use the constructor argument to instance variable mapping that I define within the constructor?
Is there another way to define this mapping such that spring-data-mongodb will properly construct my object, or is my only option to break the naming convention?
.
Exception in thread "main" org.springframework.data.mapping.model.MappingException: No property a_property found on entity class com.recorder.TestRecorder$TestObject to bind constructor parameter to!
at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:90)
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:70)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:229)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:209)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:173)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:169)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:72)
at org.springframework.data.mongodb.core.MongoTemplate$ReadDbObjectCallback.doWith(MongoTemplate.java:1820)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:1542)
at org.springframework.data.mongodb.core.MongoTemplate.findAll(MongoTemplate.java:1064)
at com.recorder.TestRecorder.main(TestRecorder.java:43)
tl;dr
We need to rely on constructor argument names to match field names to find out which field of the document to pull in. If you want to customize this use #Value("#root.field_name") on the constructor argument.
Long story
If you're using a constructor with arguments to let Spring Data instantiate the given class using this constructor we have to hand parameters to the constructor upon invocation. To find out which document field we have to hand in, we need to inspect the matching property for potential field name customization. See the following example:
#Document
class MyEntity {
#Field("foo")
private String myField;
public MyEntity(String myField) {
this.myField = myField;
}
}
In this case we need to pipe the field foo into the constructor and there's no way to find out about this if we don't somehow can obtain a reference to the property. If the constructor parameter name was something different, how should we reliably find out which field value should actually be used as argument? The example you've shown in your question can never work out of the box, as your document would contain a m_property field and there's absolutely no way to find out you actually want that to be injected, except adding more explicit configuration.
To customize this behavior you can use Spring's #Value annotation and inject a custom document field into the constructor. The document itself is available through the #root variable. So you could easily alter my sample above to:
#Document
class MyEntity {
#Field("foo")
private String myField;
public MyEntity(#Value("#root.foo") String somethingDifferent) {
this.myField = somethingDifferent;
}
}
I'd strongly recommend that you add custom field names to your properties as well as you don't want to expose your property naming conventions to the database. The usage pf #Value is briefly mentioned in the reference docs but I've created a ticket to improve the docs and make this more obvious.
You can use some custom converters (and remove #PersistenceConstructor):
// DB => Java
package com.recorder.converters;
public class TestObjectReadConverter implements Converter<DBObject, TestObject>
{
public TestObject convert(final DBObject source) {
return new TestObject((Integer) source.get("m_property"));
}
}
.
// JAVA => DB
package com.recorder.converters;
public class TestObjectWriteConverter implements Converter<TestObject, DBObject>
{
public DBObject convert(final TestObject source) {
return new BasicDBObjectBuilder("m_property", source.property()).get();
}
}
Don't forget to declare those (xml config):
<mongo:mapping-converter base-package="com.recorder">
<mongo:custom-converters>
<mongo:converter>
<bean class="com.recorder.converters.TestObjectReadConverter" />
</mongo:converter>
<mongo:converter>
<bean class="com.recorder.converters.TestObjectWriteConverter"/>
</mongo:converter>
</mongo:custom-converters>
</mongo:mapping-converter>
see this reference
Side note: this is a work around, I don't think naming convention are meant to be so tight that you need to work around. Perhaps it's time for your group to "rethink" those naming convention (for productivity sake in that case).

Categories