Deși aveam în plan să scriu despre ele, dar având în vedere că au trecut vreo 4 luni și ceva decând au apărut și nu am ce să aduc nou, m-am gândit să vad cum se descurcă cu una din cele mai comune lucruri într-o aplicație, deserializarea unui răspuns json.
Ca API vom folosi unul dummy care va returna o lista de ToDo-uri cu true sau false, în funcție dacă au fost efectuate sau nu și le vom agrega după acestă condiție.
Primul lucru este să creăm un record care să aibă câmpurile json-ului din răspuns.
{ "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }
Deci record-ul va avea un userId și un id de tip int, precum un câmp title de tip string si un completed boolean.
record Todo( @JsonProperty("userId") int userId, @JsonProperty("id") int id, @JsonProperty("title") String title, @JsonProperty("completed") boolean completed) {}
Întrucât folosim Jackson pentru deserializare și record-urile fiind o funcționalitate în preview, va fi nevoie să specificam că acele câmpuri sunt chiar cele din json. Fără ele, Jackosn va arunca o exceptie:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException
Cu record-ul creat după strucutura json-ului, este nevoie să facem call-ul la api, folosind clinetul http.
String DATA_URL = "https://jsonplaceholder.typicode.com/todos"; var httpClient = HttpClient.newHttpClient(); var httpRequest = HttpRequest.newBuilder() .uri(URI.create(DATA_URL)) .build(); var httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); var body = httpResponse.body();
Codul este simplu, nu are nimic special. Întrucât este un raspuns de tip text, sunt interesat să îl primesc așa cum este și îl pun într-o variabilă
Urmează etapa de deserializare și agregare
var mapper = new ObjectMapper(); Todo[] todos = mapper.readValue(body, Todo[].class); //because in json representation, there is an array of todos //System.out.println(todos[1].title()); var doneTodos = Arrays.stream(todos) .filter(Todo::completed) .count(); System.out.println("Completed: " + doneTodos); System.out.println("Not completed: " + (todos.length - doneTodos));
Avem un array de todo-uri, lucru pe care trebuie sa il specificam metodei readValue()
, împreună cu ceea ce vrem să deserializăm, în cazul nostru, variabila body, mai precis, conținutul ei.
Urmează o simplă filtrare dupa todo-urile completed. Aici se vede unul din avantajele de a folosi record-uri. Avem acces la propietați, exect ca la clase cu getteri și setteri, dar în cazul record-urilor sunt generate de compilator. Plus altele.
Marele final reprezinta afișarea la consolă 🙂
Ca pe final de articol, adaugarea lor în Java este binevenită și va reduce mult zgomotul de cod. Codul complet:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.company; | |
import com.fasterxml.jackson.annotation.JsonProperty; | |
import com.fasterxml.jackson.databind.ObjectMapper; | |
import java.io.IOException; | |
import java.net.URI; | |
import java.net.http.HttpClient; | |
import java.net.http.HttpRequest; | |
import java.net.http.HttpResponse; | |
import java.util.Arrays; | |
public class Main { | |
public static void main(String[] args) throws IOException, InterruptedException { | |
// write your code here | |
String DATA_URL = "https://jsonplaceholder.typicode.com/todos"; | |
var httpClient = HttpClient.newHttpClient(); | |
var httpRequest = HttpRequest.newBuilder() | |
.uri(URI.create(DATA_URL)) | |
.build(); | |
var httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); | |
var body = httpResponse.body(); | |
var mapper = new ObjectMapper(); | |
Todo[] todos = mapper.readValue(body, Todo[].class); //because in json representation in an array of todos | |
//System.out.println(todos[1].title()); | |
var doneTodos = Arrays.stream(todos) | |
.filter(Todo::completed) | |
.count(); | |
System.out.println("Completed: " + doneTodos); | |
System.out.println("Not completed: " + (todos.length – doneTodos)); | |
} | |
} | |
//https://angiejones.tech/deserializing-api-responses-into-java-records | |
record Todo( | |
@JsonProperty("userId") | |
int userId, | |
@JsonProperty("id") | |
int id, | |
@JsonProperty("title") | |
String title, | |
@JsonProperty("completed") | |
boolean completed) {} |
PS: Vom avea record-uri și in C# la versiunea 9.