JSON sending class A goes in class B - java

I am starting out on Restful web services and learning through JSON google gson. Though till now i have just made my server producer and json consumer application.
and i am loving it.
Now this is my consumer code: it has a TextMessages class object message
private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
InputStreamReader reader = null;
String name = nameTextfield.getText();
String url = "http://myurl/" + name;
try {
try {
reader = new InputStreamReader(new URL(url).openStream());
} catch (MalformedURLException ex) {
Logger.getLogger(StartClient.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (IOException ex) {
Logger.getLogger(StartClient.class.getName()).log(Level.SEVERE, null, ex);
}
TextMessages message = new Gson().fromJson(reader, TextMessages.class);
JOptionPane.showMessageDialog(this, message.getMessage(), "Welcome" + message.sumAllIntegers(),
JOptionPane.INFORMATION_MESSAGE);
}
// class TextMessages
package use;
public class TextMessages{
private String message;
private int a,b,c,d,wee;
public TextMessages(String __message){
message = __message;
}
public TextMessages(){
}
public String getMessage(){
return message;
}
public void setMessage(String __message){
message = __message;
}
}
Now server is sending JSON of class OtherTestMessage
public class TextMessage {
private String message;
//getter and setter functions
// no int a,b
.....
Why is it working right ? Shoule it be working right ? is it again languages philosophy? how does JSON convert things. Or is it donot ? Just copying data ?

The gson library is looking at the JSON and mapping what it can to the class you provide.
Both of your classes have a String called message in them. gson is taking the message element in the JSON and storing the value in both cases. In your TextMessages class, it will also store the values for a, b, etc if those elements are present in the JSON.
In the case of your TextMessage class that only has String message, the additional elements in the JSON (a,b, etc) are silently ignored if they are present.

Related

Consuming external API and converting from JSON string to bean doesn't initialize properties. NULL object attributes. Spring Boot, Maven

This is the response I get from the API.
{"get":"statistics","parameters":{"country":"romania"},"errors":[],"results":1,"response":[{"continent":"Europe","country":"Romania","population":19016885,"cases":{"new":"+4521","active":156487,"critical":431,"recovered":2606660,"1M_pop":"148707","total":2827936},"deaths":{"new":"+35","1M_pop":"3407","total":64789},"tests":{"1M_pop":"1149381","total":21857638},"day":"2022-03-24","time":"2022-03-24T07:30:04+00:00"}]}
#RestController
public class CovidTrackerRestController {
#GetMapping("/hello")
public String showCovidInformation() {
// connect to a covid database
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://covid-193.p.rapidapi.com/statistics?country=romania"))
.header("X-RapidAPI-Host", "covid-193.p.rapidapi.com")
.header("X-RapidAPI-Key", "mykey")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = null;
try {
response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// get the information
String responseString = response.body();
System.out.println(responseString);
Response romaniaData = null;
try {
romaniaData = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.readValue(responseString, Response.class);
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// format the information
System.out.println(romaniaData);
// send the information to html page
return "/tracker";
}
}
And this is my Bean class which is annotated with #Bean in the configurator class alonside the RestTemplate bean. Other properties such as Cases, Deaths etc are configured same as Response class except being declared as #Bean in the configurator because from what I know once I declare a class #Bean then other references contained automatically become beans as well.
#JsonIgnoreProperties(ignoreUnknown = true)
public class Response {
#JsonProperty("country")
private String country;
#JsonProperty("cases")
private Cases cases;
#JsonProperty("deaths")
private Deaths deaths;
#JsonProperty("day")
private String day;
#JsonProperty("time")
private String time;
#JsonProperty("test")
private Tests tests;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
Your java class needs to be exact representation of received json. Let's call it Wrapper:
public class Wrapper {
#JsonProperty("response")
private List<Response> responses;
public List<Response> getResponses() {
return this.responses;
}
public void setResponses(List<Response> responses) {
this.responses = responses;
}
#Override
public String toString() {
return "Wrapper{" +
"responses=" + responses +
'}';
}
}
I am omiting some properties - get, results, etc. It looks you don't need them. Then deserialization will look like this:
Wrapper data = null;
try {
data = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.readValue("json", Wrapper.class);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(data);
Few notes:
If json property name matches field name in class, there is no need for #JsonProperty
For tests field annotation should be - #JsonProperty("tests"). Property is tests, not test
If you really want to throw the rest of the data, and only need response property, then you need to write custom deserializer and work the json tree. You can see how to do it in my answer here, or this guide, for example. Like this you can parse the response json to your class, even if their structures do not match.
Yes, your class should be like this:
public class ResponseWrapper {
public List<Response> response;
public setResponse(List<Response> response) {
this.response= response;
}
public List<Response> getResponse() {
return response;
}
}
And class Response is your class as you published it. Your class have to have the same structure as JSON

Task manager (java)

Program must accept requests to add and remove tasks from the list through the server. After starting, server accepts connections in an infinite loop and reads from them a line containing json of the form:
{ "type": "ADD", "task": "Название задачи" }
where type is the type of operation (ADD or REMOVE) and task is the task itself. After processing the request, a list of all tasks should be displayed in the console. After connecting, my console gives null. What can be wrong?
Server class:
public class TodoServer {
public TodoServer(int port, Todos todos) {
while (true) {
try (ServerSocket serverSocket = new ServerSocket(port);
Socket clientSocket = serverSocket.accept();
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) {
System.out.println("New connection accepted");
final String json = in.readLine();
Gson gson = new Gson();
String type = gson.fromJson("\"type\"", String.class);
String task = gson.fromJson("\"task\"", String.class);
if (type.equals("ADD")) {
todos.addTask(task);
} else if (type.equals("REMOVE")) {
todos.removeTask(task);
}
System.out.println(todos.getAllTasks());
} catch (IOException e) {
System.out.println("Соединение разорвано");
}
}
}
public void start() throws IOException {
int port = 8989;
System.out.println("Starting server at " + port + "...");
}
}
Task class:
public class Todos {
static ArrayList <String> tasks = new ArrayList<>();
public void addTask(String task) {
tasks.add(task);
Collections.sort(tasks);
}
public void removeTask(String task) {
tasks.remove(task);//...
}
public String getAllTasks() {
return tasks.toString();
}
public ArrayList<String> getListTask() {
return tasks;
}
}
The Main class which the server starts:
public class Main {
public static void main(String[] args) throws IOException {
Todos todos = new Todos();
TodoServer server = new TodoServer(8989, todos);
server.start();
}
}
From what you've shown here, your parsing and use of JSON is the issue. As a starting point, you read a String json but then do nothing with it.
You'll want to parse that value into an object, and then access values out of it (like you would a dictionary or map). How to do that with GSON should have plenty of documentation and examples readily available.
If you are using an IDE for development, I also recommend using this as a great opportunity for trying the debugger out - setting breakpoints, inspecting values, etc!
It would be better to define a simple POJO to represent a task:
#Data
class MyTask {
private String type;
private String task;
}
Here #Data is a Lombok annotation which provides the boilerplate code of getters/setters/default constructor/toString/hashCode/equals.
Then the instance of such POJO is deserialized from JSON abd processed as needed:
final String json = in.readLine();
MyTask task = new Gson().fromJson(json, MyTask.class);
if ("ADD".equals(task.getType())) {
todos.addTask(task.getTask());
} else if ("REMOVE".equals(task.getType())) {
todos.removeTask(task.getTask());
}
System.out.println(todos.getAllTasks());

Deserialization of Untrusted Data in JMS Security Issue

I am using a bean for sending mails in my Java EE application. My class for sending mails is generated through xDoclet. Class code is given below
public void onMessage(javax.jms.Message message) {
MapMessage mapMsg = (MapMessage) message;
String toEmailAddress = mapMsg.getString("toAddress");
String ccEmailAddress = mapMsg.getString("ccAddress");
String from = mapMsg.getString("from");
String subject = mapMsg.getString("subject");
String content = mapMsg.getString("body");
}
Now, I have got some security issues in checkmarx for this class as - Deserialization of Untrusted Data in JMS at lines
String toEmailAddress = mapMsg.getString("toAddress");
String ccEmailAddress = mapMsg.getString("ccAddress");
Base on the Checkmarx query for Deserialization of Untrusted Data in JMS, add a try catch block when casting and check if message is not an instance of ObjectMessage:
public void onMessage(javax.jms.Message message) {
try {
if !(message instanceOf ObjectMessage){
MapMessage mapMsg = (MapMessage) message;
String toEmailAddress = mapMsg.getString("toAddress");
String ccEmailAddress = mapMsg.getString("ccAddress");
String from = mapMsg.getString("from");
String subject = mapMsg.getString("subject");
String content = mapMsg.getString("body");
}
}
catch {}
finally {}
}
I solved this issue in checkmarx adding the validation instanceof TextMessage in onMessage implementation:
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
....
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}

Spring HttpServerErrorException custom response body not being serialized

I have a Controller like this example:
#RestController
#RequestMapping(value = "/risk", produces = {MediaType.APPLICATION_JSON_VALUE})
public class CalculationController {
#RequestMapping(value = "/calculate", method = RequestMethod.POST)
public CalculationResult calculate(InputFields i) {
try {
return calcService.calculate(i);
} catch (CustomException custEx) {
throw new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR,
null,
null,
getReportLogAsBytes(custEx), //for some reason not working when serialized in Json
Charset.defaultCharset());
}
}
private byte[] getReportLogAsBytes(CustomException e) {
try {
return objectMapper.writeValueAsBytes(e.getReportLog()); //
} catch (JsonProcessingException e1) {
throw new RuntimeException("Unable to serialize Simulation report log to bytes ", e1);
}
}
class CustomException extends Exception {
private List<String> reportLog;
public CustomException(List<String> reportLog) {
super();
this.setReportLog(reportLog);
}
public List<String> getReportLog() {
return reportLog;
}
public void setReportLog(List<String> reportLog) {
this.reportLog = reportLog;
}
}
}
When posting the inputs to the controller and a CustomException occurs, I instantiate HttpServerErrorException using the constructor that accepts a byte array for responseBody. I basically give it a list of String error messages converted to byte array.
The problem is the response body still does not show the list of errors messages I passed to it. I tried looking for examples on using HttpServerErrorException with response body but cannot seem to find any... Any help would be greatly appreciated.
You throw your HttpServerErrorException but don't handle it in the proper way.
Read this: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

Jackson Parsing with java

I really hate to do this, but I have two questions: can Jackson 2.7.3 parse the following url and can do I have to parse every part of the JSON?
Here is the code I am working with so far:
public class Song {
private String tracks;
private String album;
private String images;
public void setTracks(String tracks){
this.tracks=tracks;
}
public void setAlbum(String album){
this.album= album;
}
public void setImages (String images){
this.images= images;
}
}
And
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
ObjectMapper mapper = new ObjectMapper();
Document doc = Jsoup.connect("http://api.spotify.com/v1/search?q=track:" + finalSong + "%20artist:" + finalArtist+"%20" + "&type=track").ignoreContentType(true).get();
String title = String.valueOf(doc.body().text());
Song obj = mapper.readValue(String.valueOf(title), Song.class);
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.start();
return null;
}
All I need is the "preview_url" and one of the "images" url towards the top
the JSON is located at https://api.spotify.com/v1/search?q=track:Ready%20To%20Fall%20artist:rise%20against%20&type=track.
Do you necessary need to map your Json response into a class?
If not you can get your desired values as following e.g. for preview_url
You can use readTree to map the json result into a tree of nodes.
There after you can use findPath to search for the property you looking for.
In the case of image it contains an array. Thus if you want to select a specific item from that list you get use get to select the specific item you want.
example
JsonNode readTree = mapper.readTree(body);
for (JsonNode node : readTree.findPath("items")) {
System.out.println(node.findPath("images").get(2));
System.out.println(node.findPath("preview_url"));
}

Categories