I migrated from spring to spark while ago and now I'm stuck at something basic.
When I make a POST request sending data in the body I want to have the JAVA object back in the controller..
In spring I used to do
#RequestBody User user
And it was "filled" automatically..
Now with spark I have the method:
request.body();
But that gives me a serialized string like this:
id=7&name=Pablo+Mat%C3%ADas&lastname=Gomez&githubUsername=pablomatiasgomez
So how can I get the User DTO ?
Of course, the User class has the properties
id
name
lastname
githubUsername
AFAIK, Spark does not offer this functionality. When I used it for a small pet-project, I wrote some small utility methods to parse the URL encoded string into a POJO like this:
import com.google.gson.Gson;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.LinkedHashMap;
import java.util.Map;
public class Test {
private static final Gson GSON = new Gson();
public static <T> T convert(String urlencoded, Class<T> type) {
try {
Map<String, Object> map = asMap(urlencoded);
String json = GSON.toJson(map);
return GSON.fromJson(json, type);
}
catch (Exception e) {
e.printStackTrace(); // TODO log
return null;
}
}
public static Map<String, Object> asMap(String urlencoded) throws UnsupportedEncodingException {
return asMap(urlencoded, "UTF-8");
}
#SuppressWarnings("unchecked")
public static Map<String, Object> asMap(String urlencoded, String encoding) throws UnsupportedEncodingException {
Map<String, Object> map = new LinkedHashMap<>();
for (String keyValue : urlencoded.trim().split("&")) {
String[] tokens = keyValue.trim().split("=");
String key = tokens[0];
String value = tokens.length == 1 ? null : URLDecoder.decode(tokens[1], encoding);
String[] keys = key.split("\\.");
Map<String, Object> pointer = map;
for (int i = 0; i < keys.length - 1; i++) {
String currentKey = keys[i];
Map<String, Object> nested = (Map<String, Object>) pointer.get(keys[i]);
if (nested == null) {
nested = new LinkedHashMap<>();
}
pointer.put(currentKey, nested);
pointer = nested;
}
pointer.put(keys[keys.length - 1], value);
}
return map;
}
public static void main(String[] args) {
String payload = "id=7&name=Pablo+Mat%C3%ADas&lastname=Gomez&githubUsername=pablomatiasgomez";
User user = convert(payload, User.class);
System.out.println(user);
}
}
class User {
long id;
String name;
String lastname;
String githubUsername;
#Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", lastname='" + lastname + '\'' +
", githubUsername='" + githubUsername + '\'' +
'}';
}
}
Running this Test class will print the following on your console:
User{id=7, name='Pablo Matías', lastname='Gomez', githubUsername='pablomatiasgomez'}
Note that this also work when a User has a nested structure in it, say, an Address which is composed of several other fields. jus separate the fields with "."'s like this:
public class Test {
// ... same code ...
public static void main(String[] args) {
String payload = "id=7&name=Pablo+Mat%C3%ADas&lastname=Gomez&githubUsername=pablomatiasgomez&" +
"address.street=Coolsingel&address.number=42a&address.city=Rotterdam";
User user = convert(payload, User.class);
System.out.println(user);
}
}
class User {
long id;
String name;
String lastname;
String githubUsername;
Address address;
#Override
public String toString() {
return "User{" +
"\n id=" + id +
"\n name='" + name + '\'' +
"\n lastname='" + lastname + '\'' +
"\n githubUsername='" + githubUsername + "'" +
"\n address=" + address + "\n" +
'}';
}
}
class Address {
String street;
String number;
String city;
#Override
public String toString() {
return "Address{" +
"street='" + street + '\'' +
", number='" + number + '\'' +
", city='" + city + '\'' +
'}';
}
}
which will print:
User{
id=7
name='Pablo Matías'
lastname='Gomez'
githubUsername='pablomatiasgomez'
address=Address{street='Coolsingel', number='42a', city='Rotterdam'}
}
EDIT
And if the payload contains a list of, say Users, you could do something like this:
public class Test {
private static final Gson GSON = new Gson();
public static <T> T convert(String urlencoded, Type type) {
try {
Map<String, Object> map = asMap(urlencoded);
String json = GSON.toJson(containsList(map) ? map.values() : map);
return GSON.fromJson(json, type);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static boolean containsList(Map<String, Object> map) {
return !map.isEmpty() && new ArrayList<>(map.keySet()).get(0).contains("[");
}
public static Map<String, Object> asMap(String urlencoded) throws UnsupportedEncodingException {
return asMap(urlencoded, "UTF-8");
}
#SuppressWarnings("unchecked")
public static Map<String, Object> asMap(String urlencoded, String encoding) throws UnsupportedEncodingException {
Map<String, Object> map = new LinkedHashMap<>();
for (String keyValue : urlencoded.trim().split("&")) {
String[] tokens = keyValue.trim().split("=");
String key = tokens[0];
String value = tokens.length == 1 ? null : URLDecoder.decode(tokens[1], encoding);
String[] keys = key.split("\\.");
Map<String, Object> pointer = map;
for (int i = 0; i < keys.length - 1; i++) {
String currentKey = keys[i];
Map<String, Object> nested = (Map<String, Object>) pointer.get(keys[i]);
if (nested == null) {
nested = new LinkedHashMap<>();
}
pointer.put(currentKey, nested);
pointer = nested;
}
pointer.put(keys[keys.length - 1], value);
}
return map;
}
public static void main(String[] args) throws Exception {
String payload = "id=7&name=Pablo Mat%C3%ADas";
User user = convert(payload, User.class);
System.out.println("single user -> " + user);
payload = "users[0].id=7&users[0].name=Pablo Mat%C3%ADas&users[1].id=42&users[1].name=Bart";
List<User> users = convert(payload, new TypeToken<List<User>>(){}.getType());
System.out.println("list of users -> : " + users);
}
}
class User {
long id;
String name;
#Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
which will print:
single user -> User{id=7, name='Pablo Matías'}
list of users -> : [User{id=7, name='Pablo Matías'}, User{id=42, name='Bart'}]
I found an easier way that doesn't involve URL encoding.
On the client, turn your javascript object to a JSON string and set a query parameter (yourObject) with it:
var obj = null;
obj = {
yourObject: JSON.stringify(currentObject)
};
$.ajax({
type: "GET",
url: "saveAnObject",
data: obj,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data) {
console.log('saveAnObject result: ' + data + ".");
},
error: function() {
},
cache: false
});
Then in Spark:
get("/saveAnObject", (req, res) - > {
String yourObjectStr = "" + req.queryParams("yourObject");
// Convert the JSON string to a POJO obj
Gson gson = new GsonBuilder().create();
YourObject pojoObj = gson.fromJson(yourObjectStr , YourObject.class);
// do something with your pojoObj object.
Related
I am trying to get a result from an API response and am able to map everything except for columnHeaders, which is an array of ColumnHeaders. I am instead getting a reference to an array. The code is below.
Response Class
public class Response {
#JsonProperty("searchApiFormatVersion")
private String searchApiFormatVersion;
#JsonProperty("searchName")
private String searchName;
#JsonProperty("description")
private String description;
#JsonProperty("columnHeaders")
private ColumnHeader[] columnHeaders;
public Response(String searchApiFormatVersion, String searchName, String description,
ColumnHeader[] columnHeaders) {
this.searchApiFormatVersion = searchApiFormatVersion;
this.searchName = searchName;
this.description = description;
this.columnHeaders = columnHeaders;
}
public Response(){
}
public String getSearchApiFormatVersion() {
return searchApiFormatVersion;
}
public void setSearchApiFormatVersion(String searchApiFormatVersion) {
this.searchApiFormatVersion = searchApiFormatVersion;
}
public String getSearchName() {
return searchName;
}
public void setSearchName(String searchName) {
this.searchName = searchName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public ColumnHeader[] getColumnHeaders() {
return columnHeaders;
}
public void setColumnHeaders(ColumnHeader[] columnHeaders) {
this.columnHeaders = columnHeaders;
}
#Override
public String toString() {
return "Response{" +
"searchApiFormatVersion='" + searchApiFormatVersion + '\'' +
", searchName='" + searchName + '\'' +
", description='" + description + '\'' +
", totalRowCount=" + totalRowCount +
", returnedRowCount=" + returnedRowCount +
", startingReturnedRowNumber=" + startingReturnedRowNumber +
", basetype='" + basetype + '\'' +
", columnCount=" + columnCount +
", columnHeaders=" + columnHeaders +
'}';
}
}
ColumnHeader class
public class ColumnHeader {
#JsonProperty("text")
private String text;
#JsonProperty("dataType")
private String dataType;
#JsonProperty("hierarchy")
private int hierarchy;
#JsonProperty("parentName")
private String parentName;
#JsonProperty("isEntity")
private Boolean isEntity;
#JsonProperty("isEset")
private Boolean isEset;
public ColumnHeader(String text, String dataType, int hierarchy, String parentName, Boolean isEntity, Boolean isEset) {
this.text = text;
this.dataType = dataType;
this.hierarchy = hierarchy;
this.parentName = parentName;
this.isEntity = isEntity;
this.isEset = isEset;
}
public ColumnHeader(){
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public int getHierarchy() {
return hierarchy;
}
public void setHierarchy(int hierarchy) {
this.hierarchy = hierarchy;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public Boolean getEntity() {
return isEntity;
}
public void setEntity(Boolean entity) {
isEntity = entity;
}
public Boolean getEset() {
return isEset;
}
public void setEset(Boolean eset) {
isEset = eset;
}
#Override
public String toString() {
return "ColumnHeader{" +
"text='" + text + '\'' +
", dataType='" + dataType + '\'' +
", hierarchy=" + hierarchy +
", parentName='" + parentName + '\'' +
", isEntity=" + isEntity +
", isEset=" + isEset +
'}';
}
}
Service Class
public class BudgetEffortResponseService {
Logger logger = LoggerFactory.getLogger(Response.class);
public Response getResponseFromStringJsonApiResponse(String stringJsonResponse) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper(); //Used to map objects from JSON values specified in Award under #JsonProperty annotation
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JSONObject stringJsonResponseTurnedIntoJsonObject = new JSONObject(stringJsonResponse);
logger.info("stringJsonResponseTurnedIntoJsonObject: " + stringJsonResponseTurnedIntoJsonObject);
return objectMapper.readValue(stringJsonResponseTurnedIntoJsonObject.toString(), Response.class);
}
}
Main Class
#SpringBootApplication
public class EtlApplication {
public static final String API_USERNAME = System.getenv("API_USERNAME");
public static final String API_PASSWORD = System.getenv("API_PASSWORD");
public static final String API_PREFIX = System.getenv("API_PREFIX");
public static final String API_PATH = System.getenv("API_PATH");
public static void main(String[] args) throws URISyntaxException, JsonProcessingException {
Logger logger = LoggerFactory.getLogger(EtlApplication.class);
logger.info("--------------------Starting process--------------------");
AwardRepository awardRepository = new AwardRepository();
AwardService awardService = new AwardService();
ApiResponseRowService apiResponseRowService = new ApiResponseRowService();
ApiResponseRepository apiResponseRepository = new ApiResponseRepository();
BudgetEffortResponseService budgetEffortResponseService = new BudgetEffortResponseService();
Date startDateForApiPull = new GregorianCalendar(2023, Calendar.FEBRUARY, 1).getTime();
Date endDateForApiPull = new GregorianCalendar(2023, Calendar.FEBRUARY, 2).getTime();
logger.info("============Starting BudgetEffort API pull from Huron===============");
HttpResponse<String> budgetEffortHttpResponse = apiResponseRepository.getHttpResponseFromApi(startDateForApiPull,
endDateForApiPull, 1, -1, API_PREFIX, API_PATH,
API_USERNAME, API_PASSWORD);
logger.info("BudgetEffortHttpResponse: " + budgetEffortHttpResponse);
logger.info("============End of BudgetEffort API pull from Huron===============");
//Get body of http response string
String budgetEffortResponseString = budgetEffortHttpResponse.body();
logger.info("BudgetEffortResponseString: " + budgetEffortResponseString);
Response budgetEffortResponse = budgetEffortResponseService.getResponseFromStringJsonApiResponse(budgetEffortResponseString);
logger.info("BudgetEffortResponse: " + budgetEffortResponse);
logger.info("--------------------End of process--------------------");
}
}
The response. I'm noticing that I'm getting the reference to the array for columnHeaders and not the values. How would I get the values? Thank you.
BudgetEffortResponse: Response{searchApiFormatVersion='1.0', searchName='Personnel Details for Authorized allocations on Active Awards', description='', columnHeaders=[Lcom.example.etl.entity.budgetEffort.ColumnHeader;#7a7471ce}
The response you get is ok. And also the Array is good. The line
logger.info("BudgetEffortResponse: " + budgetEffortResponse);
uses an indirect cast to String of the Object budgetEffortResponse. In this case all toString() methods of the objects are called. What you need to do in order to print out the Objects is to implement/add the toString() method in the class com.example.etl.entity.budgetEffort.ColumnHeader
Update:
As the toString method is already implemented, the above is partially wrong. But there is a way to use a setting of the ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
// pretty print
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(budgetEffortResponse);
logger.info("BudgetEffortResponse: " + json);
I have a JSON as below
[{
"empId": 22,
"Active": true,
"dept": {
"deptID": 507,
"deptDetails": [{
"deptName": "CSE",
"CreateDate": "2010-11-15T15:27:45.263"
}]
}
}]
I am trying to read the deptName as shown below
public class TestEmp {
public static void main(String[] args) throws IOException {
String json = "[{\r\n" +
" \"empId\": 22,\r\n" +
" \"Active\": true,\r\n" +
" \"dept\": {\r\n" +
" \"deptID\": 507,\r\n" +
" \"deptDetails\": [{\r\n" +
" \"deptName\": \"CSE\",\r\n" +
" \"CreateDate\": \"2010-11-15T15:27:45.263\"\r\n" +
" }]\r\n" +
" }\r\n" +
"}]";
List<Map<String, Object>> lstMpAffOffers = convertJsonToListMap(json);
for (Map<String, Object> map : lstMpAffOffers) {
Map<String, Object> deptMap = (Map<String, Object>) map.get("dept");
List<DeptDetail> deptDetail = (List<DeptDetail>) deptMap.get("deptDetails");
System.out.println(deptDetail.get(0).getDeptName());
}
}
public static List<Map<String, Object>> convertJsonToListMap(String json) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, new TypeReference<List<Map<String, Object>>>() {
});
}
}
DeptDetail.java
public class DeptDetail {
private String deptName;
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getCreateDate() {
return CreateDate;
}
public void setCreateDate(String createDate) {
CreateDate = createDate;
}
private String CreateDate;
}
When i try to read the deptName i am getting the following error
Exception in thread "main" java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.DeptDetail
You need to explicitly convert the "deptDetails" object to a list of your DeptDetail class
public class TestEmp {
public static void main(String[] args) throws IOException {
String json = "[{\r\n" +
" \"empId\": 22,\r\n" +
" \"Active\": true,\r\n" +
" \"dept\": {\r\n" +
" \"deptID\": 507,\r\n" +
" \"deptDetails\": [{\r\n" +
" \"deptName\": \"CSE\",\r\n" +
" \"CreateDate\": \"2010-11-15T15:27:45.263\"\r\n" +
" }]\r\n" +
" }\r\n" +
"}]";
List<Map<String, Object>> lstMpAffOffers = convertJsonToListMap(json);
ObjectMapper objectMapper = new ObjectMapper();
for (Map<String, Object> map : lstMpAffOffers) {
Map<String, Object> deptMap = (Map<String, Object>) map.get("dept");
List<DeptDetail> deptDetail = objectMapper.convertValue(deptMap.get("deptDetails"), new TypeReference<List<DeptDetail>>(){})
System.out.println(deptDetail.get(0).getDeptName());
}
}
public static List<Map<String, Object>> convertJsonToListMap(String json) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, new TypeReference<List<Map<String, Object>>>() {
});
}
}
First, cast it into object array then cast into DeptDetail for each element
Object[] objects=(Object[])deptMap.get("deptDetails");
List<DeptDetail> deptDetails = new ArrayList<DeptDetail>();
for (Object obj : objects) {
DeptDetail deptDetail = (DeptDetail)obj;
deptDetails.add(deptDetail);
}
I have the following Test class:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ibm.cio.cloud.cost.model.ElasticResponse;
import com.jayway.jsonpath.JsonPath;
#JsonIgnoreProperties(ignoreUnknown = true)
public class TestJSONPaths {
private static final String json = "{\"hits\":{\"total\":1,\"hits\":[{\"_id\":\"oEE4j2QBXCNPxFWHqq3i\",\"_score\":1.0,\"_source\":{\"status\":\"SUCCESSFUL\",\"reason\":\"OK, Single ACTIVE status can process\"}}]}}";
public static void main(String[] args) {
List<String> strippedJSON = JsonPath.read(json, "$.hits.hits._source");
ElasticResponse response = null;
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
try {
System.out.println("From this json string:" + strippedJSON + "\n");
response = mapper.readValue(strippedJSON.toString(), ElasticResponse.class);
System.out.println("ElasticDocument=" + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(response.getDocuments()));
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Here is the ElasticResponse class def:
public class ElasticResponse {
private List<ElasticDocument> documents;
public List<ElasticDocument> getDocuments() {
return documents;
}
public void setDocuments(List<ElasticDocument> documents) {
this.documents = documents;
}
}
public class ElasticDocument {
private String _id;
private String status;
private String reason;
... getters/setters
}
I'm trying to get a ElasticDocument object mapped from the JSON given but it's throwing the following errors below. I'm attempting to filtered out the JSON to simply be: [{_source document values }]. This error occurs on the very first line in the Main class. How can I map this Json?
[DEBUG] Evaluating path: $['hits']['hits']['_source']
Exception in thread "main" com.jayway.jsonpath.PathNotFoundException: Expected to find an object with property ['_source'] in path $['hits']['hits'] but found 'net.minidev.json.JSONArray'. This is not a json object according to the JsonProvider: 'com.jayway.jsonpath.spi.json.JsonSmartJsonProvider'.
at com.jayway.jsonpath.internal.path.PropertyPathToken.evaluate(PropertyPathToken.java:71)
at com.jayway.jsonpath.internal.path.PathToken.handleObjectProperty(PathToken.java:81)
at com.jayway.jsonpath.internal.path.PropertyPathToken.evaluate(PropertyPathToken.java:79)
at com.jayway.jsonpath.internal.path.PathToken.handleObjectProperty(PathToken.java:81)
at com.jayway.jsonpath.internal.path.PropertyPathToken.evaluate(PropertyPathToken.java:79)
at com.jayway.jsonpath.internal.path.RootPathToken.evaluate(RootPathToken.java:62)
at com.jayway.jsonpath.internal.path.CompiledPath.evaluate(CompiledPath.java:53)
at com.jayway.jsonpath.internal.path.CompiledPath.evaluate(CompiledPath.java:61)
at com.jayway.jsonpath.JsonPath.read(JsonPath.java:187)
at com.jayway.jsonpath.internal.JsonContext.read(JsonContext.java:102)
at com.jayway.jsonpath.internal.JsonContext.read(JsonContext.java:89)
at com.jayway.jsonpath.JsonPath.read(JsonPath.java:502)
at com.ibm.cio.cloud.cost.TestJSONPaths.main(TestJSONPaths.java:18)
The exception is due to the jsonpath returning an array instead of an object, so if you fix the jsonpath to look like this:
$.hits.hits[*]._source
Then it will evaluate properly. However, this probably still doesn't do what you want it to do.. The JsonPath.read() will deserialise the JSON for you. But you have to watch out with this:
public class Test {
private static final String json = "{\"hits\":{\"total\":1,\"hits\":[{\"_id\":\"oEE4j2QBXCNPxFWHqq3i\",\"_score\":1.0,\"_source\":{\"status\":\"SUCCESSFUL\",\"reason\":\"OK, Single ACTIVE status can process\"}}]}}";
public static void main(String[] args) {
List<ElasticDocument> docs = JsonPath.read(json, "$.hits.hits[*]._source");
System.out.println("elasticDoc: " + docs.get(0));
}
public static class ElasticDocument {
public String _id;
public String status;
public String reason;
#Override
public String toString() {
return "ElasticDocument{" +
"_id='" + _id + '\'' +
", status='" + status + '\'' +
", reason='" + reason + '\'' +
'}';
}
}
}
Looks like it works, however the docs List is now actually a List of Maps. Apparently It's possible to register JsonPath with Jackson but I can't make it work
Alternatively you can use Jackson to deserialise the JSON, then you should create an object model that matches the json structure and then you can use the ObjectMapper to do the deserialisation
public class Test {
private static final String json = "{\"hits\":{\"total\":1,\"hits\":[{\"_id\":\"oEE4j2QBXCNPxFWHqq3i\",\"_score\":1.0,\"_source\":{\"status\":\"SUCCESSFUL\",\"reason\":\"OK, Single ACTIVE status can process\"}}]}}";
public static void main(String[] args) {
System.out.println("From this json string:" + json + "\n");
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
try {
HitsResource hitsResource = mapper.readValue(json, HitsResource.class);
System.out.println("jackson elasticDoc: " + hitsResource.hitsParent.hits.get(0).source);
} catch (Exception e) {
e.printStackTrace();
}
}
public static class HitsResource {
#JsonProperty("hits")
public HitsParent hitsParent;
#Override
public String toString() {
return "HitsResource{" +
"hitsParent=" + hitsParent +
'}';
}
}
public static class HitsParent {
#JsonProperty("total")
public Long total;
#JsonProperty("hits")
public List<Hits> hits;
#Override
public String toString() {
return "HitsParent{" +
"total=" + total +
", hits=" + hits +
'}';
}
}
public static class Hits {
#JsonProperty("_id")
public String id;
#JsonProperty("_score")
public BigDecimal score;
#JsonProperty("_source")
public ElasticDocument source;
#Override
public String toString() {
return "Hits{" +
"id='" + id + '\'' +
", score=" + score +
", source=" + source +
'}';
}
}
public static class ElasticDocument {
#JsonProperty("_id")
public String _id;
#JsonProperty("status")
public String status;
#JsonProperty("reason")
public String reason;
#Override
public String toString() {
return "ElasticDocument{" +
"_id='" + _id + '\'' +
", status='" + status + '\'' +
", reason='" + reason + '\'' +
'}';
}
}
}
I want to convert the following JSON string to a Java object:
{
"user": {
"0": {
"firstName": "Monica",
"lastName": "Belluci"
},
"1": {
"firstName": "John",
"lastName": "Smith"
},
"2": {
"firstName": "Owen",
"lastName": "Hargreaves"
}
}
}
To convert this to Java object I've created the following classes:
class User {
private Map<String, MyObject> user = new HashMap<>();
//Getter and Setter is here
}
class MyObject {
private String firstName;
private String lastName;
//Getters and Setters are here
}
I'm using Jackson library to convert JSON to Java. Here is how I'm using the Jackson for conversion:
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(jsonString, User.class);
The problem is that with this conversion above the Map inside the User object is always empty. What am I doing wrong?
Thanks in advance.
I think it should work. I've executed this code and it works fine. Here is my example.
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class TestJackson {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String testJson = "{\n" + " \"user\": {\n" + " \"0\": {\n" + " \"firstName\": \"Monica\",\n" + " \"lastName\": \"Belluci\"\n" + " },\n" + " \"1\": {\n" + " \"firstName\": \"John\",\n" + " \"lastName\": \"Smith\"\n" + " },\n" + " \"2\": {\n" + " \"firstName\": \"Owen\",\n" + " \"lastName\": \"Hargreaves\"\n" + " }\n" + " }\n" + "}";
User readValue = mapper.readValue(testJson, User.class);
System.out.println("readValue = " + readValue);
}
}
and the User.class:
import java.util.HashMap;
import java.util.Map;
class User {
private Map<String, MyObject> user = new HashMap<String, MyObject>();
public Map<String, MyObject> getUser() {
return user;
}
public void setUser(Map<String, MyObject> user) {
this.user = user;
}
#Override
public String toString() {
return "User{" +
"user=" + user +
'}';
}
}
class MyObject {
private String firstName;
private String lastName;
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;
}
#Override
public String toString() {
return "MyObject{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
Use can done with the help of gson library.
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JsonToJava {
public static void main(String[] args) throws IOException {
try(Reader reader = new InputStreamReader(JsonToJava.class.getResourceAsStream("/Server2.json"), "UTF-8")){
Gson gson = new GsonBuilder().create();
Person p = gson.fromJson(reader, YourPOJOClass.class);
System.out.println(p);
}
}
}
visit this link hope this helps :)
I had one additional issue, that i faced when converting JSON to Java POJO: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of class out of START_ARRAY token ...
If anyone faces this issue, this is because JSON is expecting an object {} but it sees an array [{}] within the JSON String that is passed in
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(jsonString, User.class);
To fix
User[] user = mapper.readValue(jsonString, User[].class);
Reference: https://stackoverflow.com/a/33515796/4167786
You can try below code, It works fine..
public class User {
private Map<String, Map<String, String>> user;
public Map<String, Map<String, String>> getUser() {
return user;
}
public void setUser(Map<String, Map<String, String>> user) {
this.user = user;
}
}
public class JsonCast {
public static void main(String args[]) {
String response = "{\"user\" : {\"0\": {\"firstName\": \"Monica\",\"lastName\": \"Belluci\"},\"1\": { \"firstName\": \"John\",\"lastName\": \"Smith\"}}}";
ObjectMapper mapper = new ObjectMapper();
try {
User user = mapper.readValue(response, User.class);
System.out.println(user.getUser().get("0"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Let's say i make a call to a thrid party API to get a object Task and I get the following JSON String in return:
{
"tasks": [
{
"id": 1,
"code": "CODE",
"description": "Dummy Task",
"withConfirmation": false,
"resource": {
"id": "abcdef12-fe14-57c4-acb5-1234e7456d62",
"group": "Doctor",
"firstname": "Toto",
"lastname": "Wallace",
},
{
"id": 2,
"code": "CODE",
"description": "Dummyyy Taaask",
"withConfirmation": false
}
]
}
In the returned json we have a Task which can be joined with a Resource.
In our system, a Task is as the following:
#JsonAutoDetect
public class Task implements Serializable {
private Integer id;
private String code = "BASIC";
private String description;
private boolean withConfirmation = false;
/**
* CONSTRUCTOR
*/
public Task() {
}
public Integer getId() {
return id;
}
#JsonProperty
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
#JsonProperty
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#JsonProperty
public boolean isWithConfirmation() {
return withConfirmation;
}
public void setWithConfirmation(boolean withConfirmation) {
this.withConfirmation = withConfirmation;
}
public String toString() {...
}
}
and a Resource looks like that:
public class Resource implements Serializable {
...
private String firstname;
private String lastname;
private MedicalGroup group; // id + name + description
private Set<Task> tasks = new HashSet<Task>(0);
...
// getters and setters and toString etc.
...
}
So the major difference, aside from the field names is that a Task does not contain any Resource but the relation is rather in the opposite direction which means that a Resource can hold n Task.
What would be for this case the best way to serialize the returned json object from the third party and convert/map it to a pojo from my own system?
I'm currently reading Gson doc in order to try it but any suggestion is welcomed.
This code has to be easily reusable cause it's going to be needed inside multiple projects.
It is not full working code, because i have no idea how you want to work with Resource. Should Json create new resource or try to find already existing one. How will you create MedicalGroup from json, because it is not enuogh data for that. I was going to ask this in comments, but there is not enough space. And here is demo how you can try to solve most of the problems except the Resources to/from json mapping.
Main idea is to add #JsonAnyGetter public Map<String, Object> getAdditionalProperties() and #JsonAnySetter public void setAdditionalProperty(String name, Resource value) in your Task POJO:
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
HashMap<String, Object> map= new HashMap<>();
// IMPORTANT
// here we can try to find resource that has this task
// and export its info to json like this:
// CHANGE THIS
Resource res = new Resource();
res.firstname = "Toto";
res.lastname = "Wallace";
// IMPORTANT END
map.put("resource", res);
return map;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Resource value) {
// IMPORTANT
// Here you have to create or find appropriate Resource in your code
// and add current task to it
System.out.println(name+" "+ value );
}
FULL Demo:
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
public class Main3 {
private static String json = "{\n" +
" \"tasks\": [\n" +
" {\n" +
" \"id\": 1,\n" +
" \"code\": \"CODE\",\n" +
" \"description\": \"Dummy Task\",\n" +
" \"withConfirmation\": false,\n" +
" \"resource\": {\n" +
" \"id\": \"abcdef12-fe14-57c4-acb5-1234e7456d62\",\n" +
" \"group\": \"Doctor\",\n" +
" \"firstname\": \"Toto\",\n" +
" \"lastname\": \"Wallace\"\n" +
" }},\n" +
" {\n" +
" \"id\": 2,\n" +
" \"code\": \"CODE\",\n" +
" \"description\": \"Dummyyy Taaask\",\n" +
" \"withConfirmation\": false\n" +
" }\n" +
" ]\n" +
" }";
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
TasksList tl = mapper.readValue(json, TasksList.class);
String result = mapper.writeValueAsString(tl);
System.out.println(result);
}
private static class TasksList {
#JsonProperty(value = "tasks")
private List<Task> tasks;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Resource implements Serializable {
#JsonProperty(value = "firstname")
private String firstname;
#JsonProperty(value = "lastname")
private String lastname;
// HAVE NO IDEA HOW YOU GONNA MAP THIS TO JSON
// private MedicalGroup group; // id + name + description
private Set<Task> tasks = new HashSet<Task>(0);
#Override
public String toString() {
return "Resource{" +
"firstname='" + firstname + '\'' +
", lastname='" + lastname + '\'' +
", tasks=" + tasks +
'}';
}
}
#JsonAutoDetect
public static class Task implements Serializable {
private Integer id;
private String code = "BASIC";
private String description;
private boolean withConfirmation = false;
/**
* CONSTRUCTOR
*/
public Task() {
}
public Integer getId() {
return id;
}
#JsonProperty
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
#JsonProperty
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#JsonProperty
public boolean isWithConfirmation() {
return withConfirmation;
}
public void setWithConfirmation(boolean withConfirmation) {
this.withConfirmation = withConfirmation;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
HashMap<String, Object> map= new HashMap<>();
// IMPORTANT
// here we can try to find resource that has this task
// and export its info to json like this:
// CHANGE THIS
Resource res = new Resource();
res.firstname = "Toto";
res.lastname = "Wallace";
// IMPORTANT END
map.put("resource", res);
return map;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Resource value) {
// IMPORTANT
// Probably here you have to create or find appropriate Resource in your code
// and add current task to it
System.out.println(name+" "+ value );
}
#Override
public String toString() {
return "Task{" +
"id=" + id +
", code='" + code + '\'' +
", description='" + description + '\'' +
", withConfirmation=" + withConfirmation +
'}';
}
}
}
you can use Gson library by google to convert Json to Pojo Class.
new Gson().fromJson(jsonString,Response.class);