cloud endpoint not respecting Jackson annotations - java

I have a class Organization with few date fields as follows.
public class Organization {
private String _id;
private String name;
#JsonDeserialize(using=JsonDateDeserializer.class)
#JsonSerialize(using=JsonDateSerializer.class)
private Date createdTime;
// getters and setters
}
To handle the date in simple way on client side I convert date to long and send it to client using these JsonSerializer as follows
public class JsonDateSerializer extends JsonSerializer<Date>{
#Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
gen.writeNumber(date.getTime());
}
}
I have a endpoint which will execute get organization and insert the results in response map like this.
#ApiMethod(name="organization.get", path="organizations/{organizationId}", httpMethod=HttpMethod.GET)
public Map<String, Object> organizationDetails(#Named("organizationId") String organizationId){
Organization org = DB.getOrganization("123");
Map<String, Object> response = new HashMap<String, Object>();
response.put("status", HttpServletResponse.SC_OK);
response.put("success", true);
response.put("entity", org);
return response;
}
But the end resultant JSON I see on client side is
{
"status" : 200,
"entity" : [ {
"_id" : "966a03b3-8e46-41ee-b330-6533409b2b4a",
"name" : "POKURI",
"createdTime" : "2015-05-16T15:02:31.499+05:30"
} ],
"success" : true
}
Here the date coming in some format instead of long. If I convert the same using Jackson ObjectMapper without using cloud endpoints I am getting response in expected way. Why cloud endpoints not respecting Jackson annotations? Is there a way to configure that?
Note: Even I observed that long is coming as string on client side if you use cloud endpoints. I am using appengine SDK 1.9.19

You aren't supposed to use Jackson annotations with Endpoints (and if you do, you have to use the repackaged version, not your own). You can do what you want with #ApiResourceProperty or #ApiTransformer, depending on how you want to do it. See the annotation documentation here.
Regarding long, it is serialized as a string due to floating point imprecision--it's not possible to represent all values of a long accurately using a double, which is what JavaScript and JSON.parse will store a numeric in, so it is always transmitted as a string. Any of our client libraries for Endpoints automatically convert them to the correct data type.

Related

How extract response to String and save it to variable

Being new to Java/JSON/REST Assured topics, I would like to extract a parameter of "token": from a JSON response body as a String and store it as variable which I could take to some other classes and use there. However, I have tried it and have not found a way. Below is part of a code which I have created at the beginning in a same manner as other requests stored in this class, but this is the first one from which I need something from the response:
public FakeTokenVO fakeToken() {
String payload = "payloadthere";
return given(specBuilder.fakeTokenRequestSpecification()) .
body(payload)
.log().all()
.when()
.post(RestApiRoutes.FAKE_URI)
.then()
.log().all()
.extract()
.response()
.as(FakeTokenVO.class);
}
Don't mind about the payload and those VO classes as it is stored as data model somewhere else.
Response from the request made looks like this:
{
"createTokenResponse": {
"createTokenSuccess": {
"token": "token_with_somewhere_about_700_characters"
}
}
}
Here is how I have tried to modify it to get the part of response which I need later (the token to authorize other requests):
#Test
public void fakeToken()
{
String payload = "payloadthere";
String token = given(specBuilder.fakeTokenRequestSpecification())
.body(payload)
.log().all()
.when()
.post(RestApiRoutes.FAKE_URI)
.then()
.log().all()
.extract()
.response()
.body().path("createTokenResponse.createTokenSuccess.token");
System.out.print(token);
}
This test returns me a value which I needed, but I do not know how to implement it as a method instead of test. Please help how should I approach it? What am I missing there? I tried to search for answers, but I haven't found a solution yet or do not know how to implement it in my part of the code.
I assume that you can get your response as a String. So all you need to do is to parse your Json String. For that you can use any available Json parser. The most popular ones are Json-Jackson (also known as Faster XML) or Gson (by Google). Both are very well known and popular. (My personal preference is Jackson, but it is a matter of opinion).
However, For simplistic cases like this I wrote my own utility (a thin wrapper over Jackson library) that allows you to parse Json String very simply without learning relatively complex libraries. With my utility your code may look like this:
try {
Map<String, Object> map = JsonUtils.readObjectFromJsonString(jsonStr, Map.class);
Map<String, Object> innerMap = map.get("createTokenResponse");
Map<String, Object> innerMap2 = map.get("createTokenSuccess");
String token = innerMap.get("token");
} catch (IOException e) {
e.printStacktrace();
}
Or you can create your own classes such as
public class TokenResult {
String token;
//getter and setter
}
public class TokenHolder {
private TokenResult createTokenSuccess;
//setter and getter
}
public class TokenResponse {
private TokenHolder createTokenResponse;
//setter and getter
}
And than your code may look like this:
try {
TokenResponse response = JsonUtils.readObjectFromJsonString(jsonStr, TokenResponse .class);
String token = response.getCreateTokenResponse().getCreateTokenSuccess().getToken();
} catch (IOException e) {
e.printStacktrace();
}
Here is a Javadoc for JsonUtils class. This Utility comes as part of Open Source MgntUtils library written and maintained by me. You can get the library as maven artifact on Maven Central here or on the github (including source code and javadoc)

Changing POJO structure on returning to API call

I have assigned values to my POJO class to return as response body to an API call. I want to change my POJO structure, like by making the POJO as value to an object on top of the POJO.
For example:
Home POJO:
{
int number;
String address;
}
Values are assigned to the POJO and I can send it as response body to my API call, but I want my response body to be:
{
output: {
number: 1,
address: "chennai"
}
}
I know that I can achieve this using JSON-Object or using a HashMap or a parent POJO (Note: I do not want to create a POJO Output just for this case).
Is there any other way to serialize the POJO like this using Jackson or any other methods for Java with Spring?
You can apply #JsonRootName annotation on your class specifying "output" as its value.
#JsonRootName("output")
public class MyPojo {
private int number;
private String address;
// all-args constructor, getters, etc.
}
But this annotation alone would not have any impact on serialization. In order to make to instruct Jackson to use it, we need to enable the serialization feature WRAP_ROOT_VALUE:
Feature that can be enabled to make root value (usually JSON Object but can be any type) wrapped within a single property JSON object, where key as the "root name", ...
Usage example:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
System.out.println(
mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(new MyPojo(1000, "foo"))
);
Output:
{
"output" : {
"number" : 1000,
"address" : "foo"
}
}

Parse JSON without object name in Java

I am trying to parse this JSON which is coming as the response to a REST API call. Can you please help me parsing it as key value pairs?
The object names are not present. There is nesting as well. There seems to be no new line between records.
The aim is to extract this data and load it into a database.
[
{
"cc_emails":["feedback#xyz.com"],
"fwd_emails":[],
"reply_cc_emails":["feedback#xyz.com"],
"fr_escalated":false,
"spam":false,
"email_config_id":6000038087,
"group_id":6000110481,
"priority":1,
"requester_id":6010410791,
"responder_id":6002817857,
"source":1,
"company_id":null,
"status":2,
"subject":"fare",
"to_emails":["feedback#xyz.com"],
"product_id":null,
"id":45043,
"type":null,
"due_by":"2016-03-12T08:58:02Z",
"fr_due_by":"2016-03-08T08:58:02Z",
"is_escalated":false,
"description":"Dear xyze Team,\r\n\r\nWhy r u increased fair again and againasas0mail.gmail.com</a>.<br>\n",
"custom_fields":
{
"category":null,
"issue":null,
"route_id":null,
"phone_number":null,
"department":null,
"booking_id":null
},
"created_at":"2016-03-07T08:58:02Z",
"updated_at":"2016-03-07T08:58:03Z",
// ...... repeat
}
]
The best way to do this would be to use http://www.jsonschema2pojo.org/
Enter your json there
Change source type to JSON
set the correct class name and package.
The resulting pojo can be directly mapped from the json
If you are using resttemplate to hit the api then you can use getForObject to automatically set the pojo from the output.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject-java.lang.String-java.lang.Class-java.lang.Object...-
Using gson you can do this quite simply.
Do a class to match the fields in the json something like:
public class Example {
private List<String> cc_emails;
private List<String> fwd_emails;
private List<String> reply_cc_emails;
private Boolean fr_escalated;
private Boolean spam;
private Integer email_config_id;
...
private CustomFields custom_fields;
private String created_at;
private String updated_at;
}
Then you need to do another to map the custom fields
public class CustomFields {
private String category;
...
}
And using json you can parse it like this:
Type type = new TypeToken<Collection<Example>>(){}.getType();
new Gson().fromJson(json,type);
You have to exaplain to Gson it's a list, if it was a single object it would be this:
new Gson().fromJson(json,Example.class);
This is the aproach I usually take, also in the dates java.sql.Timestamp class might also parse it, you would need to try it though.
You can use Gson (https://github.com/google/gson) or Jackson (https://github.com/FasterXML/jackson) and deserialize it to a Map.

JSON number to String

One of my JSON content has number for entityTypeId, how to change this to a String ?
eg change 1 to "1".
JSON
[
{
entityTypeId: 3,
entityTypeName: "Branch of Legal Entity"
},
{
entityTypeId: 1,
entityTypeName: "Legal Entity"
},
{
entityTypeId: 2,
entityTypeName: "Notional Entity"
}
]
REST API
#GET
#Path(value = "/entityTypes")
#Produces(MediaType.APPLICATION_JSON)
#Override
public List<EntityType> getEntityTypes() {
return commonBusiness.getEntityTypes();
}
JPA Entity
public class EntityType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="ENTITY_TYPE_ID")
private long entityTypeId;
#Column(name="ENTITY_TYPE_NAME")
private String entityTypeName;
Update:
Many of you asked why I need to change to a String. I use this JSON data to render a drop-down. This drop down value (entityTypeId) saves in the DB in a number column successfully. But when I load the view page the drop-down is not loaded with that value. Other drop downs work which has both those values as String.
Earlier I raised a separated issue
Angularjs - dropdown - Should the key/value pair always be a string data type
In your EntityType class you would need to change the type of the entityTypeId to be a String, but there might be an impact if you do that, so you need to think about what that column accepts in the database. The bigger question is why do you want to change your data type to be a String.
I think you should make 'entityTypeId' as String instead of long.
I suggest to use a DTO which contains the data as String, and do the conversion in the service layer.
I think you should try this. json = json.replace (/:(\d+)([,}])/g, ':"$1"$2');
If you have used json library.
If I understand correctly, the requirement here is to have the REST service json response to have entityType as a number.
This can be achieved by creating a json response using a custom serializer.
#GET
#Path(value = "/entityTypes")
#Produces(MediaType.APPLICATION_JSON)
#Override
public Response getEntityTypes() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("entityModule",
Version.unknownVersion());
module.addSerializer(EntityCollection.class, new EntityTypeJsonSerializer());
mapper.registerModule(module);
String responseJson = mapper.writeValueAsString(commonBusiness.getEntityTypes());
return Response
.status(Status.OK)
.entity(responseJson)
.type(MediaType.APPLICATION_JSON).build();
}
Create an interim collection class
public class EntityCollection{
private List<EntityType> entityTypes;
}
Custom Serializer:
public class EntityTypeJsonSerializer extends JsonSerializer<EntityCollection> {
#Override
public void serialize(EntityCollection entityTypes, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
// JSON parsing goes here
jgen.writeString(String.valueOf(entityType.get(entityTypeId)));
}
}
This will make your JPA entity and response JSON independent.
I don't think using number is an issue in a select.
Check out this example I've created.
<div ng-app="myApp" ng-controller="myController">
<select ng-model="entity.selected">
<option value="{{ent.entityTypeId}}" ng-repeat="ent in entityTypes">{{ent.entityTypeName}}</option>
</select>
Selected entity id {{entity.selected}}
</div>
JSFiddle
I've also updated your other question. Let me know if this is what you were looking for.

How to send multiple json objects in a post request using retrofit?

I have this json object of key value pairs that needs to be sent in a post request using retrofit
{
"Criteria":{
"DisciplineId":0,
"SeasonId":0,
"Leagues":[
],
"StartDate":"06 Sep 2013",
"EndDate":"14 Dec 2013",
"RoundId":0,
"GroupId":0,
"MatchesScores":3
},
"SearchInfo":{
"PageNumber":1,
"PageSize":20,
"Sort":1,
"TotalRecords":542
}
}
I was thinking of creating a POJO that matches the gson definition of the json object then using the setters in the POJO class to set the value for each key value pair.
So I would have something like this
#FormUrlEncoded
#POST("/getMatches")
void getMatches(#Field("Criteria") Criteria criteria,#Field("SearchInfo") SearchInfo searchInfo, Callback<JSONKeys> keys);
Am I on the right track?
How can I achieve this seeing that there are two nested json objects within the json object as well as a json array with one of these objects?
You could create a request class that contains both of those. As long as the names of the member variables match the json (or you use SerializedName) the conversion happens automatically.
class MyRequest{
#SerializedName("Criteria") Criteria criteria;
#SerializedName("SearchInfo") SearchInfo searchInfo;
}
Where Criteria is:
class Criteria {
#SerializedName("DisciplineId") int disciplineId;
#SerializedName("SeasonId") int seasonId;
#SerializedName("Leagues") List<Integer> leagues; // Change Integer to datatype
#SerializedName("StartDate") String startDate;
#SerializedName("EndDate") String endDate;
#SerializedName("RoundId") int roundId;
#SerializedName("GroupId") int groupId;
#SerializedName("MatchesScores") int matchesScores;
}
And SearchInfo is:
class SearchInfo{
#SerializedName("PageNumber") int pageNumber;
#SerializedName("PageSize") int pageSize;
#SerializedName("Sort") int sort;
#SerializedName("TotalRecords") int totalRecords;
}
To use try (see here):
#POST("/getMatches")
public void getMatches(#Body MyRequest request, Callback<Boolean> success);
Retrofit uses Gson internally and will automatically convert your MyRequest Object to the json format you've described in your question.
Note: Usually it's convention to name the json keys as lowercase-with-underscore, and java with camelcase. Then instead of using SerializedName everywhere, you set your key naming convention when creating your gson object (see here):
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()

Categories