Grails domain constructor with JSON parameter did not work - json

I have such controller in my grails project:
def submit() {
def json = request.JSON
Share share = new Share(json)
share.save(flush: true, failOnError: true)
}
Class Share looks like this:
class Share {
String timestamp
String deviceName
String originMessage
Share(JSONObject originalMessage) {
println "Run JSON constructor"
println "$originalMessage"
originMessage = originalMessage.toString()
timestamp = originalMessage.timestamp
deviceName = originalMessage.device
}
It recives JSON request and try to persist in data base.
I get such error in my console on failOnError:
Field error in object 'com.entity.Share' on field 'deviceName': rejected value [null];
Field error in object 'com.entity.Share' on field 'originMessage': rejected value [null]; codes
A lot of dancing around find one possible way: in controller convert JSON to string and pass it in constructor where parameter would be type String and then parse it back in JSON using JSON converter. But why I can not pass JSON Object as parameter correctly. What happens?

I don't see much point in declaring this constructor because domain classes already have an implicit constructor that takes a Map argument. You could call this constructor with the JSONObject because this class implements Map, e.g.
class Share {
String timestamp
String deviceName
String originMessage
}
def json = request.JSON
Share share = new Share(json)
The reason for these errors
Field error in object 'com.entity.Share' on field 'deviceName': rejected value [null];
Field error in object 'com.entity.Share' on field 'originMessage': rejected value [null]; codes
is that your JSONObject instance does not have non-null properties named deviceName or originMessage.
Either you need to figure out why these properties are missing, or allow these properties to be null by adding the following constraints to the domain class
class Share {
String timestamp
String deviceName
String originMessage
static constraints = {
deviceName nullable: true
originMessage nullable: true
}
}

Related

Eliminate duplicate Json elements and retrieve element names starting with capital letters spring boot/java

I'm developing a Rest Client using Spring Boot and Spring Framework (spring-boot-starter-parent 2.1.6.RELEASE)
I have a class representing a response object as shown below:
public class ValidateResponse {
private String ResponseCode;
private String ResponseDesc;
//getters and setters
//constructors using fields
//empty constructor
}
I'm creating a web-hook for an external api and I need to return a JSON object to for a specific endpoint (the JSON object properties must start with uppercase(s)). I'm calling returning the object from a PostMapping method nested in a RequestMapping root path:
#PostMapping("hooks/validate")
public ValidateResponse responseObj(#RequestHeader Map<String, String> headersObj) {
ValidateResponse response = new ValidateResponse("000000", "Success");
logger.info("Endpoint = hooks/validate | Request Headers = {}", headersObj);
return response;
}
However, when I hit the endpoint from postman I'm getting duplicate varialbes
{
"ResponseCode": "000000",
"ResponseDesc": "Success",
"responseCode": "000000",
"responseDesc": "Success"
}
I understand that the pojo-json conversion is handled by spring but I don't understand why the conversion is yielding duplicate variables.
Note: I know the ResponseDesc and the ResponseCode are not declared using the best standards for naming variables (camelCasing).
I've done some digging and according to the Java Language Specification
An identifier is an unlimited-length sequence of Java letters and Java digits, the first of which must be a Java letter.
and
The "Java letters" include uppercase and lowercase ASCII Latin letters A-Z (\u0041-\u005a), and a-z (\u0061-\u007a), and, for historical reasons, the ASCII underscore (_, or \u005f) and dollar sign ($, or \u0024). The $ character should be used only in mechanically generated source code or, rarely, to access pre-existing names on legacy systems.
So, I'm assuming its syntactically correct to define a variable using the Camelcase format [Need clarification on this].
I'm considering having to create the JSON object manually but I'd like to know the cause of this behaviour first. Any pointers are appreciated.
Jackson deserializes all the public fields that it comes across. However if you want Jackson to return the response in your expected element names (in your case elements starting with capital letters), make the fields private and annotate them with the #JsonProperty(expected_name_here). Your class file will typically looks as shown below
public class ValidateResponse {
#JsonProperty("ResponseDesc")
private String responseCode;
#JsonProperty("ResponseDesc")
private String responseDesc;
//getters and setters
//constructors using fields
//empty constructor
}
Note: The getters and setters for these fields should be public, otherwise Jackson won't see anything to deserialize in the class.
public class ValidateResponse {
#JsonProperty("ResponseDesc")
public String responseCode;
#JsonProperty("ResponseDesc")
public String responseDesc;
//getters and setters
//constructors using fields
//empty constructor
}
This must fix your problem, however I do not know the reason as it requires deep Jackson investigation.
EDIT
I found out the reason.
The field got duplicated because in you case you had:
2 public fields named in upper case -> they are to be processed by jackson
2 getters getResponseCode and getResponseDesc -> they are to be resolved
as accessors for properties responseCode and responseDesc accordingly.
Summing this up - you have 4 properties resolved by Jackson. Simply making your fields private will resolve your issue, however I still advise using JsonProperty approach.
I added a com.google.code.gson dependency in the projects pom.xml file to configure Spring Boot to use Gson (instead of the default jackson).
The Json object returned from the hooks/validate endpoint must have its property names starting with a capital letter. Using a java class to generate the response object was resulting to camelCased property names so I resolved to create the JSON response object manually. Here's the code for creating the custom JSON object:
public ResponseEntity<String> responseObj(#RequestHeader Map<String, String> headersObj) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
JsonObject response = new JsonObject();
response.addProperty("ResponseCode", "00000000");
response.addProperty("ResponseDesc" , "Success");
logger.info("Endpoint = hooks/validate | Request Headers = {}", headersObj);
return ResponseEntity.ok().headers(responseHeaders).body(response.toString());
}
Note The JSON object is returned as a String so the response from the endpoint must have an additional header to define MediaType to inform the calling system that the response is in JSON format:
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
then add the header to the response:
return ResponseEntity.ok().headers(responseHeaders).body(response.toString());

How does one parse a complex domain object to JSON object in grails?

Okay first off , I am extremely new to grails. And I am quite stuck at converting a domain object to JSON.
My domain class looks as follows
class MoneyTransfer {
Account fromAccount
Date sourceTransactionDate
TransactionStatus sourceTransactionStatus
String sourceTransactionMessage
Account toAccount
Date destinationTransactionDate
TransactionStatus destinationTransactionStatus
String destinationTransactionMessage
double amount
String note
Status status
PianoUser creator
String errorMessage
// predefined grails date create & modified & version
Date dateCreated
Date lastUpdated
String uniqueId
}
How does one convert such a domain class's object to JSON object ?
I tried using grails.converters.JSON and grails.converters.deep.JSON as follows
class MyTransferController{
def xyz(){
MoneyTransfer monetTransferInstance = getMoneyTransferInstance();
def moneyTransferJson = fundTrasnferInstance as JSON //doesnot work
}
}
How do I convert my domain class's object to JSON Object? Any suggestion would be appreciated.
Try this
import grails.converters.JSON
class MyTransferController {
def xyz() {
MoneyTransfer moneyTransferInstance = getMoneyTransferInstance()
render moneyTransferInstance as JSON
}
}

Gson deserialisation returns null

I have a problem on converting JSON string to object in Android. Here are the JSON structure and Java classes:
JSON:
{
"code":"SUCCEED",
"message":"",
"result":{
"ccahUserId": 111,
"ccahUserName":"your_name",
"userFirstName":"your_first_name",
"userLastName":"your_last_name",
//others
}
Java classes:
public class Result<T>{
public String code;
public String message;
public T result;
}
public class DeviceSetting
{
public long ccahUserId;
public String ccahUserName;
public String userFirstName;
public String userLastName;
//other members
}
Activity:
Gson gson = new Gson();
Result<DeviceSetting> setting = gson.fromJson(result, Result<DeviceSetting>.class);
When I deserialise the JSON string, code and message field were good but result field is null.
I am not familiar with Gson yet, so please help how to fix this problem?
Thanks in advance.
Likely the result field is null because it relies on the type parameter for Result.
From the GSON documentation for Gson.fromJson(JsonElement, Class<T>) (bolding is mine):
This method deserializes the Json read from the specified parse tree
into an object of the specified type. It is not suitable to use if the
specified class is a generic type since it will not have the generic
type information because of the Type Erasure feature of Java. Therefore, this method should not be used if the desired type is a generic type. Note that this method works fine if the any of the fields of the specified object are generics, just the object itself should not be a generic type. For the cases when the object is of generic type, invoke fromJson(JsonElement, Type).

FlexJSON - JSONDeserializer String Cannot be caste as Boolean

I am using FlexJson within my play framework application but at the point I am trying to deseralize the json string it throws a java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean:
User user = new JSONDeserializer<User>()
.use(null, User.class).deserialize(body);
Body is the json string passed into the controller using standard jquery/ajax and
where User has the following boolean value declared:
public Boolean isCurrentUser;
Any ideas as to what I am doing wrong?
Thanks
In Json, Boolean is a type. Your JSon is:
{"user_id":"18","isCurrentUser":"true","title":"mr","description":"description"}
when it should be:
{"user_id":"18","isCurrentUser":true,"title":"mr","description":"description"}
Note that true is not a String, but a boolean. The parser fails because it finds a String instead of the expected boolean type. Fix the JSon generation to add a boolean, not a String.

Is . char allowed in JSON field name?

Is . char allowed in JSON field name?
java.lang.IllegalArgumentException: instance.id is not a valid JSON field name.
at com.google.gson.JsonFieldNameValidator.validate(JsonFieldNameValidator.java:52)
Atleast gson library seems to be complaining. But I couldn't find anything in json spec.
Note that I have serialized name annotation to avoid issue in java field name.
#SerializedName("instance.id")
private String instanceId;
Update:
It is a bug in serializedname and This is the fix I did:
#SdeSerializedName("instance.id")
private String instanceId;
and
new GsonBuilder().setFieldNamingStrategy
(new FieldNamingStrategy() {
public String translateName(final Field field) {
final SdeSerializedName annotation = field.getAnnotation(SdeSerializedName.class);
return ((null != annotation) && null != annotation.value()) ? annotation.value() : field.getName();
}
})
It is allowed in JSON itself, but (if I understand the GSON documentation correctly) the error message is because it can't map instance.id to a Java class member of the same name.
Have a look at Following thread about a similar problem mapping field names:
http://groups.google.com/group/google-gson/tree/browse_frm/month/2010-05/e575bb65cdd30410?rnum=31&_done=/group/google-gson/browse_frm/month/2010-05?&pli=1
Since the dot "." is already the separator between an object and a member name in javascript (this is where json originates), it cannot be a valid field name.