Recently at a test, I came across a problem to do some processing after querying the response data from a restful service. I had to write a web service consumer and do some processing using Java.
While I was able to consume the service using Http classes from jdk, I didn't knew of any way to map the response json in their respective pojo's, without using Jackson's or other external mapper libraries.
Now I have been trying to do that. Until now I have tried to change the incoming Json to byte array and deserialize and map into the pojo, but that didn't worked. I remember with JAX-B unmarshalling was possible but it has been carved out of jdk after java 8, and I have been working with higher version JDK 11.
I also tried getting the response as streams but then data processing does not remains equally straightforward, as it would have been in case of working with model classes. I am in split, any way out of it would be very appreciable ..
HttpRequest req = HttpRequest
.newBuilder(new URI("https://jsonmock.hackerrank.com/api/transactions/search?userId=4"))
.GET().build();
//HttpResponse<byte[]> response = HttpClient.newHttpClient().send(r, BodyHandlers.ofByteArray());
HttpResponse<Stream<String>> response = HttpClient.newHttpClient().send(req, BodyHandlers.ofLines());
Stream<String> responseStream = response.body();
Natively you can do it using Regex and plenty complex custom logic assuming you handling the JSON as string changing the response Line as below.
HttpResponse response = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
I assume that the models you could use are below
Location Model
`
public class Location {
private int id;
private String address;
private String city;
private int zipCode;
public Location(int id, String address, String city, int zipCode) {
this.id = id;
this.address = address;
this.city = city;
this.zipCode = zipCode;
}
public Location() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getZipCode() {
return zipCode;
}
public void setZipCode(int zipCode) {
this.zipCode = zipCode;
}
}
`
User Model
`
public class User {
private int id;
private int userId;
private String userName;
private double timestamp;
private String txnType;
private String amount;
private Location location;
private String ip;
public User(int id, int userId, String userName, double timestamp, String txnType, String amount, Location location, String ip) {
this.id = id;
this.userId = userId;
this.userName = userName;
this.timestamp = timestamp;
this.txnType = txnType;
this.amount = amount;
this.location = location;
this.ip = ip;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public double getTimestamp() {
return timestamp;
}
public void setTimestamp(double timestamp) {
this.timestamp = timestamp;
}
public String getTxnType() {
return txnType;
}
public void setTxnType(String txnType) {
this.txnType = txnType;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
}
`
Generally Java has not any build in parser. You may find this below
Java Built-in data parser for JSON or XML or else
The most simple library you might find is described here
https://www.oracle.com/technical-resources/articles/java/json.html
and you might find it as maven dependency below
https://mvnrepository.com/artifact/org.json/json
To do that you would have to write your own Json parser that would do the same as Jackson, Gson Simple Json or any other external Json library. In other words you would be re-inventing the wheel. While it might be very interesting it would be pretty useless. That is why those libraries have been written in the first place.
Related
I am currently building a rest api that lets the user enter a recipe and describe it. I am using spring-boot as backend and angularjs as frontend.
This is springboot recipe file
package com.example.springboot;
import com.example.springboot.Recipe;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
#Entity // This tells Hibernate to make a table out of this class
public class Recipe {
public Recipe(){
}
public Recipe(Integer id, String name, String description, String type, Integer preptime, Integer cooktime, String content, Integer difficulty){
this.id = id;
this.name = name;
this.description = description;
this.type = type;
this.preptime = preptimee;
this.cooktime = cooktime;
this.content = content;
this.difficulty = difficulty;
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String description;
private String type;
private Integer preptime;
#Column(columnDefinition = "TEXT")
private String content;
private Integer difficulty;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getDifficulty() {
return difficulty;
}
public void setDifficulty(Integer difficulty) {
this.difficulty = difficulty;
}
public Integer getpreptime {
return preptime;
}
public void setpreptime(Integer preptime) {
this.preptime = preptime;
}
}
I created an Endpoint where the user can edit the whole recipe. The user can edit the name , description, content and so on in the recipes/edit/{id} endpoint.
The Endpoint looks like this.
#PutMapping("/recipes/edit/{id}")
void updateRecipe(#PathVariable int id, #RequestBody Recipe recipe ) {
System.out.println("entering");
Recipe recipe_ = recipeRepository.findById(id).get();
recipe_.setName(recipe.getName());
recipe_.setDescription(recipe.getDescription());
recipe_.setType(recipe.getType());
recipe_.setpreptime(recipe.getpreptime());
recipe_.setContent(recipe.getContent());
System.out.println("entering " + recipe.getTitle());
System.out.println("entering" + recipe.getType());
System.out.println("entering" + recipe.getDescription());
System.out.println("adding");
recipeRepository.save(recipe_);
}
Now I just want to create an Endpoint which only serves the purpose for renaming the name of the recipe. This putmapping should accept a list as its input then only rename the name of the recipe.
#PutMapping("/recipes/rename")
public List<Recipe> {
System.out.println("entering renaming");
// recipe_.setName(recipe.getName()); ?
}
I don't know how I can implement this. This is what I have come up with so far. An endpoint which takes a list as a parameter.
This is the service.ts file that updates the Recipes in the edit function
service.ts:
updateRecipe (id: number, recipe: any): Observable<any > {
const url = `${this.usersUrl}/edit/${id}`;
return this.http.put(url ,recipe);
}
where the updateRecipe gets called:
save(): void {
const id = +this.route.snapshot.paramMap.get('name');
this.recipeService.updateRecipe2(id, this.recipes)
.subscribe(() => this.gotoUserList());
}
This implementation work , I don't know how I can get it work or how I can rewrite the functions so that it can update only the name of the recipe and not the whole file.
Could someone help me?
Your update the name method should look like that:
#PutMapping("...{id}")
public void updateName(#PathVariable Integer id, #RequestParam String name){
Recipe recipe = repository.findById(id).orElseThrow(...);
recipe.setName(name);
}
if you want to rename list of recipes
public void renameRecipes(String oldName, String newName){
repository.findByName(oldName)
.forEach(r -> r.setName(newName));
}
#PutMapping("recipes/rename")
public void updateNames(#PequestParam String oldName, #RequestParam String newName){
renameRecipes(oldName, newName);
}
Try that.
I am working on error handling a response mapping. Before I go ahead and map the response to my domain objects, I want to validate the response. Check for errors.
I am planning to have a Validator.java class and implement validation methods for each of the API call.
Is there any alternative way in spring to do this?
package com.people.net;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
public class UserInfo {
//Unicode check
#Pattern(regexp="[0-9a-zA-Z\\s-]+", message="chars,numbers allowed only")
String name;
int id;
#Pattern(regexp="([0-9]{10})", message="minLength=maxLength=10 only numbers")
String pin;
#Email
String email;
#Size(max=5, message="5 chars max")
String emailType;
#Size(max=5, message="5 chars max")
String addressType;
#Size(max=300, message="5 chars max")
String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public UserInfo(String name, String pin, String id) {
super();
this.id = Integer.parseInt(id);
this.name = name;
this.pin = pin;
}
public UserInfo(int id,String name, String pin, String email, String emailType, String addressType, String address) {
super();
this.id = id;
this.name = name;
this.pin = pin;
this.email = email;
this.emailType = emailType;
this.addressType = addressType;
this.address = address;
}
public UserInfo() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPin() {
return pin;
}
public void setPin(String pin) {
this.pin = pin;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmailType() {
return emailType;
}
public void setEmailType(String emailType) {
this.emailType = emailType;
}
public String getAddressType() {
return addressType;
}
public void setAddressType(String addressType) {
this.addressType = addressType;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
You should explore hibernate validator which is the reference implementation JSR-349 for bean validation. more info is here https://jcp.org/en/jsr/detail?id=349 .
Also hibernate validator is a industry standard for doing the bean validation(lots of stuff available on internet) and comes with lots of popular framework like dropwizard etc.it provide annotation based validation and allow you to write your own custom annotation based validation.
please refer on step by step guide http://www.journaldev.com/2668/spring-validation-example-mvc-validator, where author uses it with the spring.
I'm making a web app using Jersey and Jackson. I've made a response POJO of the following type:
#JsonInclude(Include.NON_NULL)
public class ResponsePojo {
private Integer id;
private String name;
private String imageUrl;
private JsonObject queryParams;
public ResponsePojo(Integer id, String name, String imageUrl, JsonObject queryParams) {
this.id = id;
this.name = name;
this.imageUrl = imageUrl;
this.queryParams = queryParams;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public JsonObject getQueryParams() {
return queryParams;
}
public void setQueryParams(JsonObject queryParams) {
this.queryParams = queryParams;
}
}
I receive a proper JSON response from the webAPI when I omit the private JsonObject queryParams; field. How can I return a JSON from inside the response POJO?
I'm receiving the following error:
Direct self-reference leading to cycle (through reference chain: my.package.Response["list"]->java.util.ArrayList[0]->my.package.ResponsePojo["queryParams"]->com.google.gson.JsonObject["asJsonObject"])
I am trying to get some the array of actors from Jira. The code for the wrapper is used in a Gson.fromJson call. I had used something similar with a json string that did not have an array in it that had the information I needed and it worked fine, so the issue seems to do with the array, but I am not 100% sure:
import com.google.gson.annotations.SerializedName;
public class JiraRoleJsonWrapper {
#SerializedName("self")
private String self;
#SerializedName("name")
private String name;
#SerializedName("id")
private int id;
#SerializedName("description")
private String description;
#SerializedName("actors")
private JiraActors[] actors;
public JiraActors[] getActors() {
return actors;
}
public void setActors(JiraActors[] actors) {
this.actors = actors;
}
public String getSelf() {
return self;
}
public void setSelf(String self) {
this.self = self;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String key) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/*
public String[] getAvatarUrls() {
return avatarUrls;
}
public void setAvatarUrls(String[] avatarUrls) {
this.avatarUrls = avatarUrls;
}
*/
}
class JiraActors {
#SerializedName("id")
private int id;
#SerializedName("displayNme")
private String displayName;
#SerializedName("type")
private String type;
#SerializedName("name")
private String name;
//#SerializedName("avatarUrl")
//private String avatarUrl;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The json it would receive:
{
"self":"http://someserver.com:8080/apps/jira/rest/api/2/project/10741/role/10002",
"name":"Administrators",
"id":10002,
"description":"A project role",
"actors":[
{
"id":12432,
"displayName":"Joe Smith",
"type":"atlassian-user-role-actor",
"name":"joesmi",
"avatarUrl":"/apps/jira/secure/useravatar?size=xsmall&ownerId=dawsmi&avatarId=12245"
},
{
"id":12612,
"displayName":"Smurfette Desdemona",
"type":"atlassian-user-role-actor",
"name":"smudes",
"avatarUrl":"/apps/jira/secure/useravatar?size=xsmall&ownerId=lamade&avatarId=10100"
},
This shows two actors and the format of the json. Please note I did not put a complete json response. It just shows two actors.
In my code, I tried the following to retrieve the actors:
InputStream is = response.getEntityInputStream();
Reader reader = new InputStreamReader(is);
Gson gson = new Gson();
JiraRoleJsonWrapper[] jiraRoleJsonWrapper = gson.fromJson(reader, JiraRoleJsonWrapper[].class);
for (JiraRoleJsonWrapper w : jiraRoleJsonWrapper) {
JiraActors[] a = w.getActors();
String name = a.getName();
It does not find getName for some reason. I am not sure why.
I figured it out.
I change the setActors to
public void setActors(ArrayList<JiraActors> actors) {
this.actors = actors;
}
Then I was able to get the array list and get access to the getName() method of JiraActors.
I am writing a Restful webservice which would receive data in the below format.
{
"myOrder": {
"submitDate": "2015-04-16T02:52:01.406-04:00",
"supplier": "Amazon",
"orderName": "Wifi Router",
"submittedBy": "Gaurav Varma",
"price": {
"value": "2000",
"currency": "USD"
},
"address": {
"name": "My home",
"address": "Unknow island",
"city": "Mainland China",
"state": "Xinjiang",
"contact": {
"firstName": "Gaurav",
"lastName": "Varma",
"phone": "000-000-0000",
"email": "test#gv.com"
}
}
}
}
To read that data I am considering Jackson or GSON frameworks. The easiest way would be to use a Java POJO which has exactly the same structure as the json request. But for me the structure of Java POJOs is different. I have four different pojo as mentioned below :
Submitter.java
- SubmittedBy
- SubmitDate
Order.java
- Supplier
- OrderName
Price.java
- Value
- Currency
Address.java
- Name
- Address
- City
- State
Contact.java
- FirstName
- LastName
- Phone
- Email
Question : Is it a way to parse the json once into five different POJOs. May be some annotation based approach where we can map json attribute to respective pojo attribute? Any framework available for it?
Thanks in advance !
I'm currently using Jackson on my project. You have the option of annotating your POJO fields with #JsonProperty or #JsonUnwrapped. You would use #JsonUnwrapped on Order, for example, and then Order would have two fields (supplier and orderName) that use #JsonProperty.
See here for more details.
You could use eclipse link moxy for this. It uses JAXB style annotations for field to JSON/XML mapping.
Moxy is part of eclipse link.
Wikipedia:
EclipseLink is the open source Eclipse Persistence Services Project
from the Eclipse Foundation. The software provides an extensible
framework that allows Java developers to interact with various data
services, including databases, web services, Object XML mapping (OXM),
and Enterprise Information Systems (EIS).
So in your code you would use it like;
Model A:
#XmlElement(name="completed_in")
public float getCompletedIn() {
return completedIn;
}
Model B:
#XmlElement(name="created_at")
#XmlJavaTypeAdapter(DateAdapter.class)
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
#XmlElement(name="from_user")
public String getFromUser() {
return fromUser;
}
Json:
{
"completed_in":0.153,
{
"created_at":"Fri, 12 Aug 2011 01:14:57 +0000",
"from_user":"stackfeed",
you can use the composition design pattern and have an instance of each object in a wrapper class. Or you can try to parse the json into a map and write code to instantiate and set the variables as needed.
You could use Jackson; I think you need a POJO to wrapp the Order and Address like
class FullOrder {
Order order;
Address address;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
With this you can easily use Jackson
String json; // your json here
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readValue(json, FullOrder.class);
And that will parse the json into your pojo. Hope it helps you
The full structure
class Submitter {
private Date submittedBy;
private Date submitDate;
public Date getSubmittedBy() {
return SubmittedBy;
}
public void setSubmittedBy(Date submittedBy) {
SubmittedBy = submittedBy;
}
public Date getSubmitDate() {
return SubmitDate;
}
public void setSubmitDate(Date submitDate) {
SubmitDate = submitDate;
}
}
class Order {
private String supplier;
private String orderName;
private Price price;
private Submitter submitter;
public Price getPrice() {
return price;
}
public void setPrice(Price price) {
this.price = price;
}
public Submitter getSubmitter() {
return submitter;
}
public void setSubmitter(Submitter submitter) {
this.submitter = submitter;
}
public String getSupplier() {
return Supplier;
}
public void setSupplier(String supplier) {
Supplier = supplier;
}
public String getOrderName() {
return OrderName;
}
public void setOrderName(String orderName) {
OrderName = orderName;
}
}
class Price {
private int value;
private int currency;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getCurrency() {
return currency;
}
public void setCurrency(int currency) {
this.currency = currency;
}
}
class Address {
private String name;
private String address;
private String city;
private String state;
private Contact contact;
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
class Contact {
String firstName;
String lastName;
long phone;
String email;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public long getPhone() {
return phone;
}
public void setPhone(long phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
class FullOrder {
Order myOrder;
Address address;
public Order getMyOrder() {
return order;
}
public void setMyOrder(Order order) {
this.order = order;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
This is structure of your json, you only need to copy it and use the Object mapper to parse the json to the pojo (FullOrder) that contains the other pojos and properties
String json; // your json here
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readValue(json, FullOrder.class);
I figured out the solution approach. Posting for other users. The complete implementation is on my blog - http://javareferencegv.blogspot.com/2015/04/parse-json-into-multiple-java-pojos.html
So basically 3 points regarding solution approach:
We use Jackson annotation - #JsonIgnoreProperties. This would make
sure only those fields in Pojo are mapped to JSON attributes. So we
read the json twice, once mapping to Order.java and then to
Submitter.java. Both gets the correspondingly mapped fields.
We use Jackson annotation - #JsonProperty. This lets us map the exact JSON attribute to a field in POJO. The annotation makes sure different named attributes in JSON and POJO are mapped.
Jackson doesn't provide any annotation to perform #JsonWrapped (The vice-versa #JsonUnwrapped is available for serialization). Hence, we map Price as an attribute in Order.java.
The main class looks like this :
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonDeserializer {
public static void main(String[] args) {
try {
// ObjectMapper provides functionality for data binding between
ObjectMapper mapper = new ObjectMapper();
String jsonString = "{\"submitDate\":\"2015-04-16\",\"submittedBy\":\"Gaurav Varma\",\"supplier\":\"Amazon\",\"orderName\":\"This is my order\","
+ "\"price\": {\"value\": \"2000\",\"currency\": \"USD\"}"
+ "}";
System.out.println("JSON String: " + jsonString);
// Deserialize JSON to java format and write to specific POJOs
Submitter submitterObj = mapper.readValue(jsonString, Submitter.class);
Order orderObj = mapper.readValue(jsonString, Order.class);
Price priceObj = orderObj.getPrice();
System.out.println("submitterObj: " + submitterObj);
System.out.println("orderObj: " + orderObj);
System.out.println("priceObj: " + priceObj);
} catch (Exception e) {
e.printStackTrace();
}
}
}