How to create POJO from JSON with nested JSONString - java

Currently I have code that is receiving a POST request with a body of
{
"EventBody": {
"message":"{\"sysData\":{\"time\":1520865496235,\"data\":{\"appUUID\":\"randomnumber\",\"userId\":\"1801\"}},\"appData\":{\"testMessage\":\"Stuff to see\",\"ackURL\":\"http:localhost/generic\"\"}}",
"client": {
"device": {
"device-id": 12345
}
}
}
}
That body if being converted to a pojo object of as such
#JsonIgnoreProperties(ignoreUnknown = true)
public class EventBody {
// If state message object as type string I do get the string, if
// I keep it GenericMessage object I am met with a null object.
#JsonProperty("message")
public GenericMessage message;
#JsonIgnoreProperties(ignoreUnknown = true)
public class GenericMessage {
#JsonProperty("sysData")
public SysData sysData;
#JsonProperty("appData")
public AppData appData;
public class SysData {
public String time;
public Data data;
public class Data {
public String appUUID;
public Integer userId;
}
}
public class AppData {
public String ackURL;
}
}
#JsonProperty("client")
public Client client;
public class Client {
#JsonProperty("device")
public Device device;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Device {
#JsonProperty("device-id")
public Integer deviceId;
}
}
}
I am running into a problem of how to parse the / delimited string formatted as a JSON object. I have had it work by moving the GenericMessage class into its own seperate class and used by a service class to parse it with this method.
public GenericMessage convertGenericMessage(String message) throws MessageException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
if(message != null) {
GenericMessage foundMessage = objectMapper.readValue(message, GenericMessage.class);
return foundMessage;
} else {
throw new MessageException("Message field empty in event Object");
}
} catch (Exception e) {
throw new MessageException("Exception while converting message to GenericMessage object: " + e.getMessage());
}
}
But this is a bit ugly and introducing extra steps that I am hoping are unnecessary. Is there a way of calling some kind of implicit constructor for GenericMessage so that is parses the JSONString object?

Related

How to sum values of jsonObject and create new jsonobject of sum inside a jsonArray in JAVA?

I have JSON Array as below and want to sum values of JSON object and create new JSON object of sum inside a JSON array:
{
"client":[
{
"member":12,
"group":"g1"
},
{
"member":17,
"group":"g2"
}
],
"client2":[
{
"member":14,
"group":"g11"
},
{
"member":175,
"group":"g22"
}
]
}
I want to sum the member value for each jsonobject inside the jsonarray and create extra json and put it inside client array. The expected json should look like below:
{
"client":[
{
"member":12,
"group":"g1"
},
{
"member":17,
"group":"g2"
},
{
"totalMember":29,
"group":"all"
}
],
"client2":[
{
"member":14,
"group":"g11"
},
{
"member":175,
"group":"g22"
},
{
"totalMember":189,
"group":"all"
}
]
}
I tried as:
mainJson.fieldNames().forEach(fn->{
JsonArray jsonArray = mainJson.getJsonArray(fn);
int id = 0;
for (int i = 0; i < jsonArray.size(); i++) {
id += jsonArray.getJsonObject(i).getInteger("id");
JsonObject jsonObject = new JsonObject().put("id",id).put("group","all");
jsonArray.add(jsonObject);
mainJson.put(fn,jsonArray);
}
});
Your expected JSON string is not normal because any JSON objects belong to the same JSON array should have the same structure, so the output JSON string should look like as below:
{
"client":[
{
"member":12,
"group":"g1"
},
{
"member":17,
"group":"g2"
},
{
"member":29,
"group":"all"
}
],
...
}
If your expected JSON string can be revised so, then here comes another way to achieve what you want by following steps with Jackson and Lambda Expression (since Java 8):
Step 1
Create POJOs and use #JsonAnySetter to serialize client and client2 to List<ClientInfo>, and use #JsonIgnore for getName() for deserialization to ignore field name.
class RootPojo {
private List<ClientInfo> clients = new ArrayList<>();
#JsonAnySetter
public void setClients(String name, List<ClientInfo> client) {
client.forEach(e -> {
e.setName(name);
});
this.clients.addAll(client);
}
//general getter and toString
}
class ClientInfo {
private String name;
private int member;
private String group;
#JsonIgnore
public String getName() {
return name;
}
//general getters, setters and toString
}
Step 2
Serialize JSON string to pre-defined POJOs with Jackson:
ObjectMapper mapper = new ObjectMapper();
RootPojo rootPojo = mapper.readValue(inputJsonStr, RootPojo.class);
System.out.println(rootPojo.toString());
Console output:
RootPojo [clients=[ClientInfo [name=client, member=12, group=g1], ClientInfo [name=client, member=17, group=g2], ClientInfo [name=client2, member=14, group=g11], ClientInfo [name=client2, member=175, group=g22]]]
Step 3
Use Lambda Expression for grouping and summation which will also add the results as new JSON objects back to original JSON string.
rootPojo.getClients()
.stream()
.collect(Collectors.groupingBy(ClientInfo::getName,
Collectors.summingInt(ClientInfo::getMember)))
.forEach((k,v) -> {
ClientInfo clientInfo = new ClientInfo();
clientInfo.setName(k);
clientInfo.setGroup("all");
clientInfo.setMember(v);
rootPojo.getClients().add(clientInfo);
});
System.out.println(rootPojo.toString());
Console output:
RootPojo [clients=[ClientInfo [name=client, member=12, group=g1], ClientInfo [name=client, member=17, group=g2], ClientInfo [name=client2, member=14, group=g11], ClientInfo [name=client2, member=175, group=g22], ClientInfo [name=client, member=29, group=all], ClientInfo [name=client2, member=189, group=all]]]
Step 4
Transform rootPojo into Map<String, List<ClientInfo> then deserialize it to output JSON string:
Map<String, List<ClientInfo>> clientMap = new HashMap<>();
rootPojo.getClients().forEach(e -> {
if (clientMap.containsKey(e.getName())) {
clientMap.get(e.getName()).add(e);
} else {
List<ClientInfo> clients = new ArrayList<>();
clients.add(e);
clientMap.put(e.getName(), clients);
}
});
String outputJsonStr = mapper.writeValueAsString(clientMap);
System.out.println(outputJsonStr);
Console output:
{"client":[{"member":12,"group":"g1"},{"member":17,"group":"g2"},{"member":29,"group":"all"}],"client2":[{"member":14,"group":"g11"},{"member":175,"group":"g22"},{"member":189,"group":"all"}]}
So, below is a full worked example using gson library (googles json parser).
First i created the class for defining the initial json file:
import java.io.Serializable;
import java.util.ArrayList;
public class ClientSer implements Serializable {
ArrayList<ClientDataSer> client;
ArrayList<ClientDataSer> client2;
public ClientSer(ArrayList<ClientDataSer> client, ArrayList<ClientDataSer> client2) {
this.client = client;
this.client2 = client2;
}
public ArrayList<ClientDataSer> getClient() {
return client;
}
public void setClient(ArrayList<ClientDataSer> client) {
this.client = client;
}
public ArrayList<ClientDataSer> getClient2() {
return client2;
}
public void setClient2(ArrayList<ClientDataSer> client2) {
this.client2 = client2;
}
}
With client data ser looking like:
public class ClientDataSer extends ClientDataParentSer {
int member;
public ClientDataSer(int member, String group) {
super(group);
this.member = member;
}
public int getMember() {
return member;
}
public void setMember(int member) {
this.member = member;
}
}
In order for gson to uses files as definitions of data structure, they need to be serialisable. I will get the why ClientDataSer extends ClientDataParentSer in a moment.
The code for reading this file, caluclating the total member value and printing it to another file is shown below:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.*;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
try (Reader reader = new FileReader("test.json")) {
// Convert JSON File to Java Object
ClientSer clientSer = gson.fromJson(reader, ClientSer.class);
ClientNewSer clientNewSer = new ClientNewSer(getNewClientData(clientSer.getClient()), getNewClientData(clientSer.getClient2()));
try {
Writer writer = new FileWriter("testNew.json");
gson.toJson(clientNewSer, writer);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static ArrayList<ClientDataParentSer> getNewClientData(ArrayList<ClientDataSer> clientDataSerList) {
ArrayList<ClientDataParentSer> clientDataSers = new ArrayList<>();
int memberCounter = 0;
for (ClientDataParentSer clientDataSer : clientDataSerList) {
clientDataSers.add(clientDataSer);
memberCounter += ((ClientDataSer)clientDataSer).getMember();
}
ClientDataNewSer clientDataNewSer = new ClientDataNewSer("all", memberCounter);
clientDataSers.add(clientDataNewSer);
return clientDataSers;
}
}
So, as you wanted client and client2 to contain a list each with 2 different obejects (one with field member and group, and the other with fields total member and group), we had to do some hierarchy stuff.
If we make a parent class containing the common field (group):
import java.io.Serializable;
public class ClientDataParentSer implements Serializable {
private final String group;
public ClientDataParentSer(String group) {
this.group = group;
}
public String getGroup() {
return group;
}
}
and then make ClientDataSer and a new class:
public class ClientDataNewSer extends ClientDataParentSer {
int member;
public ClientDataNewSer(String group, int member) {
super(group);
this.member = member;
}
public int getMember() {
return member;
}
public void setMember(int member) {
this.member = member;
}
}
extend this parent class, we can have a list of ClientDataParentSer that contain both, ie the list the output json file needs.
the class for the new object is shown below:
import java.io.Serializable;
import java.util.ArrayList;
public class ClientNewSer implements Serializable {
ArrayList<ClientDataParentSer> client;
ArrayList<ClientDataParentSer> client2;
public ClientNewSer(ArrayList<ClientDataParentSer> client, ArrayList<ClientDataParentSer> client2) {
this.client = client;
this.client2 = client2;
}
public ArrayList<ClientDataParentSer> getClient() {
return client;
}
public void setClient(ArrayList<ClientDataParentSer> client) {
this.client = client;
}
public ArrayList<ClientDataParentSer> getClient2() {
return client2;
}
public void setClient2(ArrayList<ClientDataParentSer> client2) {
this.client2 = client2;
}
}
Any questions about anything comment below.
The full project is on my github here

Exception: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token at

I am trying to display data from a web service and getting this error :
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token at...
Find below my code :
Model object :
public class Commune implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Commune [name=" + name + "]";
}
}
Main class:
public class Test {
public static void main(String[] args) throws IOException {
URL url = new URL("https://bj-decoupage-territorial.herokuapp.com/api/v1/towns");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/json");
if(connection.getResponseCode() != 200){
throw new RuntimeException("Failed : Http code : "+connection.getResponseCode());
}
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String output;
while((output = reader.readLine()) != null){
ObjectMapper mapper = new ObjectMapper();
TypeReference<List<Commune>> mapType = new TypeReference<List<Commune>>() {};
List<Commune> jsonToCommuneList = mapper.readValue(output, mapType);
for(Commune c : jsonToCommuneList) {
System.out.println(c.getName());
}
}
connection.disconnect();
}
}
Here is the data getting from the url :
{"towns":
[
{"name":"BANIKOARA"},
{"name":"GOGOUNOU"},
{"name":"KANDI"},
{"name":"KARIMAMA"}
]
}
Help me please to check what i did wrong.
Thank you
You are getting this error because you are trying to deserialize something that is not actually a JSON Array into a Collection
If you are able to change your JSON to send just a JSON Array format, it will look like this one below:
[
{"name":"BANIKOARA"},
{"name":"GOGOUNOU"},
{"name":"KANDI"},
{"name":"KARIMAMA"}
]
Otherwise, to parse it correctly you also can create a new class which will represent the town of your JSON and parse using it:
public class TownRecords implements Serializable {
private List<Commune> communes;
... getters and setters
}
You're trying to deserialize a list of Commune, but in HTTP response you're getting an object, containing such a list, but not a list itself. So, you need another wrapper object for deserialisation:
Wrapper
public class Towns implements Serializable {
private List<Commune> towns;
public Towns() {}
public List<Commune> getTowns() {
return towns;
}
public void setTowns(List<Commune> towns) {
this.towns = towns;
}
#Override
public String toString() {
return "Towns: " + towns.toString();
}
}
main app
TypeReference<Towns> mapType = new TypeReference<Towns>() {};
Towns towns = mapper.readValue(output, mapType);
System.out.println(towns);

Spring gets Null values from Retrofit

I am trying to send POST Request from Android to Spring via Retrofit from past 2 days. I have tried plenty of solutions regarding this, but nothing seems to be working. So, asking here finally to be able to get some help.
So, i am trying to send a simple Object from Android to Spring via retrofit. From android side i have verified the values sent by me and it gives me correct value.(I have debugged it via Android's debugging mode). But on spring side i got null values.
This is my code ->
function foo() -> from which i am sending my request.
private void foo() {
request.setName1("XYZ");
request.setName2("PQR");
Api.upload(request, new Callback<BasicResponse>() {
#Override
public void onResponse(Call<BasicResponse> call, Response<BasicResponse> response) {
Log.d(TAG, "onResponse: Success" + response.toString());
}
#Override
public void onFailure(Call<BasicResponse> call, Throwable t) {
Log.d(TAG, "onResponse: Failure");
}
});
}
}
my upload Function ->
public static void upload(Request request, Callback<BasicResponse> callback) {
uploadRequest api = retrofit.create(uploadRequest.class);
Call<BasicResponse> call = uploadRequest.uploadCall(POJO);
call.enqueue(callback);
}
This is my UploadRequest Interface ->
public interface uploadRequest{
#POST(UPLOAD_REQUEST)
Call<BasicResponse> uploadCall(#Body POJO pojo);
}
This is My POJO Class
public class POJO {
private String Name1;
private String Name2;
public String getName1() {
return Name1;
}
public void setName1(String name1) {
Name1 = name1;
}
public String getName2() {
return Name2;
}
public void setName2(String name2) {
Name2 = name2;
}
}
And this is my Spring Controller Method
#RequestMapping(value = "/UploadRequest",method = RequestMethod.POST,consumes = "application/json", produces = "application/json")
#ResponseBody
public void UploadImage(#RequestBody POJO pojo,HttpServletRequest request) {
if(pojo!=null){
System.out.println("pojo is not null");
System.out.println(pojo.getName1());
}
else{
System.out.println("null");
}
}
I am getting pojo is not null and inside the pojo.getName1(), the value prints is null.
Edit : Adding BasicResponse Class.
public class BasicResponse {
private boolean success = Boolean.TRUE;
private ErrorCode errorCode;
private String response;
private Integer totalCount;
private String callingUserId;
public BasicResponse() {
super();
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public ErrorCode getErrorCode() {
return errorCode;
}
public void setErrorCode(ErrorCode errorCode) {
this.errorCode = errorCode;
this.success = false;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
public void setResponse(Object response) {
if (response != null) {
this.response = GsonUtils.toGson(response);
}
}
public Integer getTotalCount() {
return totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}
public String getCallingUserId() {
return callingUserId;
}
public void setCallingUserId(String callingUserId) {
this.callingUserId = callingUserId;
}
}
Compare the response and your POJO class. The instance variables of your POJO class must be the same as in response. In your case Name1 and Name2. If they are name1, name2 in the response (which means if they do not start with capital letters, etc.), or different, it gives you NullPointerException.
Most likely Name1 and Name2 have different names in json than in your POJO and Jackson, with is used under the hood when you annotate your parameters with #RequestBody can not figure them out, so you get null values. Check out your json.

Convert the java class data into JSON format?

I am doing the Java project with spring.So I am using the Jackson library to convert to get the JSON format.
My java Class will be ,
public class ChatInteraction extends Interaction{
private int ticketId;
private String name;
private String interactionType ;
private LinkedList<InteractionInfo> interactions;
public ChatInteraction(Message response) {
super(response);
interactions = new LinkedList<InteractionInfo>();
}
public int getTicketId() {
return ticketId;
}
public void setTicketId(int ticketId) {
this.ticketId = ticketId;
System.out.println("Ticket Id for Interaction : "+this.ticketId);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("Name for Interaction : "+this.name);
}
public LinkedList<InteractionInfo> getInteractions() {
return interactions;
}
public String getInteractionType() {
return interactionType;
}
public void setInteractionType(String interactionType) {
this.interactionType = interactionType;
}
public void addInteraction(InteractionInfo interaction) {
this.interactions.add(interaction);
}
public void accept(int proxyId,String intxnId,int ticketId){
RequestAccept reqAccept = RequestAccept.create();
reqAccept.setProxyClientId(proxyId);
reqAccept.setInteractionId(intxnId);
reqAccept.setTicketId(ticketId);
System.out.println("New Chat RequestAccept Request Object ::: "+reqAccept.toString());
try{
if(intxnProtocol.getState() == ChannelState.Opened){
Message response = intxnProtocol.request(reqAccept);
System.out.println("New Chat RequestAccept Response ::: "+response.toString());
if(response != null ){
if( response.messageId() == EventAck.ID){
System.out.println("Accept new chat success !");
//EventAccepted accept = (EventAccepted)response;
//return "New chat Interaction accepted";
}else if(response.messageId() == EventError.ID){
System.out.println("Accept new chat Failed !");
//return "New chat Interaction rejected";
}
}
}else{
System.out.println("RequestAccept failure due to Interaction protocol error !");
}
}catch(Exception acceptExcpetion){
acceptExcpetion.printStackTrace();
}
}
public void join(String sessionId, String subject) {
RequestJoin join = RequestJoin.create();
join.setMessageText(MessageText.create(""));
join.setQueueKey("Resources:"); //Add the chat-inbound-key in multimedia of the optional tab values of the softphone application in CME
join.setSessionId(sessionId);
join.setVisibility(Visibility.All);
join.setSubject(subject);
KeyValueCollection kvc = new KeyValueCollection();
join.setUserData(kvc);
System.out.println("Join Request Object ::: "+join.toString());
try {
if(basicProtocol != null && basicProtocol.getState() == ChannelState.Opened){
Message response = basicProtocol.request(join);
if(response != null){
System.out.println("RequestJoin response ::: "+response);
if (response.messageId() == EventSessionInfo.ID) {
System.out.println("Join Request success !");
}else{
System.out.println("Join Request Failed !");
}
}
}else{
System.out.println("BasicChat protocol Error !");
//return "BasicChat protocol Error !";
}
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
}
I need to get only the interactionType and interactions property of this class in the JSON format like ,
{"interactionType":"invite","interactions" : [{"xx":"XX","yy":"YY"},{"xx":"XX","yy":"YY"}]}
Note :
I don't need the other properties of this class.
Also there is no SETTER for the interactions property . Instead of that I have the addInteractions() method . Does this affects any behaviour of JSON conversion ?
Also I have some other methods like accept(...) , Join(...).
I am using the jackson-all-1.9.0.jar
You can annotate the unneeded fields with #JsonIgnore - see Jackson's manual on annotations. That's what it will look like, using your code:
public class ChatInteraction extends Interaction{
#JsonIgnore
private int ticketId;
#JsonIgnore
private String name;
private String interactionType ;
private LinkedList<InteractionInfo> interactions;
You can use achieve this by using the #JsonIgnoreProperties annotation that can be used on class level.
From JavaDoc:
Annotation that can be used to either suppress serialization of
properties (during serialization), or ignore processing of JSON
properties read (during deserialization).
Example:
// to prevent specified fields from being serialized or deserialized
// (i.e. not include in JSON output; or being set even if they were included)
\#JsonIgnoreProperties({ "internalId", "secretKey" })
Example, In your case:
#JsonIgnoreProperties({ "ticketId", "name" })
public class ChatInteraction extends Interaction{
....
}
Finally I got the solution by others answers in the thread and similar answers in stackoverflow,
I marked the #JsonIgnore in the unwanted field in the sub class and super class suggested by fvu.
I have used the myObjectMapper.setVisibility(JsonMethod.FIELD, Visibility.ANY); in my objectMapper suggested in other thread like,
ObjectMapper mapp = new ObjectMapper();
mapp.setVisibility(JsonMethod.FIELD, Visibility.ANY);
try {
json = mapp.writeValueAsString(info);
info.clear();
System.out.println("Chat Info in JSON String is :::> "+json);
} catch (Exception e) {
e.printStackTrace();
}

Unable to parse JSON using GSON in playframework because of nested bean

I have created a Bean as a representation of a JSON object that I will be parsing. Problem is, the bean has nested classes. Because of which, compilation fails. I verified by removing the class. The app compiles properly. This is the error I get:
2012-11-05 19:23:30,783 ERROR ~
#6c8po9gbf
Internal Server Error (500) for request GET /favicon.ico
Oops: NullPointerException
An unexpected error occured caused by exception NullPointerException: null
play.exceptions.UnexpectedException: Unexpected Error
at play.Play.start(Play.java:545)
at play.Play.detectChanges(Play.java:629)
at play.Invoker$Invocation.init(Invoker.java:198)
at Invocation.HTTP Request(Play!)
Caused by: java.lang.NullPointerException
at play.classloading.ApplicationCompiler$2.acceptResult(ApplicationCompiler.java:266)
at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:478)
at play.classloading.ApplicationCompiler.compile(ApplicationCompiler.java:282)
at play.classloading.ApplicationClassloader.getAllClasses(ApplicationClassloader.java:424)
at play.Play.start(Play.java:505)
... 3 more
The javabean class is
import java.util.List;
import com.google.gson.Gson;
public class GsGetChallenge {
public static void main(String... args) throws Exception {
String json = "{\"response\":\n" + " {\"salt\":\n"
+ " \"zP9UFJOklQLePOqf0lSh0NgdlXWAt8qhIq4adcP1opdkz8UwVz\",\n"
+ " \"status\":\"SUCCESS\",\n"
+ " \"challenge\":\"BXuQQ2056310911\"\n" + "}}";
ResponseData responsee = new Gson().fromJson(json, ResponseData.class);
// Show it.
System.out.println(responsee.getResponse().getChallenge());
}
}
class ResponseData {
private Response response;
public static class Response {
private String salt;
private String status;
private String challenge;
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getChallenge() {
return challenge;
}
public void setChallenge(String challenge) {
this.challenge = challenge;
}
}
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
}
}
What can I do to use GSON, in such a case? I am using play 1.2.4.
move ResponseData to another .java or move ResponseData into GsGetChallenge and change it to static class GsGetChallenge. like
public static void index() {
String json = "{\"response\":\n" + " {\"salt\":\n"
...
}
static class ResponseData {
private Response response;
...

Categories