
Tworzymy obiekt Java na podstawie odpowiedzi w formacie JSON zwróconej z zewnętrznego interfejsu RESTful API.
Skąd wzięła się potrzeba generowania POJO?
Korzystając z dowolnego interfejsu RESTful API otrzymujemy w odpowiedzi obiekt zapisany w formacie JSON (ang. JavaScript Object Notation). Aby przetworzyć informacje zawarte w obiekcie JSON musimy dokonać mapowania tej odpowiedzi na obiekt POJO (ang. Plain Old Java Object). W rezultacie dostajemy proste obiekty zawierające pola oraz metody dostępowe (stery oraz getery). Należy pamiętać, że w obiektach POJO nie umieszczamy logiki biznesowej.
Generowanie POJO na podstawie JSON
W celu wygenerowania struktury obiektów POJO możemy skorzystać z darmowego narzędzia dostępnego online Plain Old Java Object. Po umieszczeniu przykładowej odpowiedzi lub schematu JSON otrzymujemy kod Java, który reprezentuje strukturę obiektów zawartych w odpowiedzi.
Załóżmy, że chcemy pobrać zawartość playlisty za pomocą YouTube API. W tym celu musimy wysłać odpowiednio spreparowane żądanie HTTP metodą GET.
https://www.googleapis.com/youtube/v3/playlistItems?playlistId=PL8A2F4D823A7D2E49&maxResults=5&part=snippet%2CcontentDetails%20&fields=nextPageToken,items(snippet(title,resourceId(videoId)))&key={YOUR_API_KEY}
W odpowiedzi dostajemy obiekt JSON o następującej treści:
{
"nextPageToken":"CAUQAA",
"items":[
{
"snippet":{
"title":"Słoń - Ugly Kid Słoń | Prod. Bent, deki DJ Flip (OFICJALNY TELEDYSK)",
"resourceId":{
"videoId":"xj7PWNS1I8k"
}
}
},
{
"snippet":{
"title":"06. Słoń - #Negatywnyrap | bit/skrecze TheReturners (OFICJALNY ODSŁUCH)",
"resourceId":{
"videoId":"BNS6n6iZ_HY"
}
}
},
{
"snippet":{
"title":"AK-47 - Jeden Krok",
"resourceId":{
"videoId":"ScjViSj6jiE"
}
}
},
{
"snippet":{
"title":"AK-47 - Znów igrasz z losem (OFFICIAL VIDEO)",
"resourceId":{
"videoId":"31GJngZbwUQ"
}
}
},
{
"snippet":{
"title":"17 Chada & Pih - Odbierz , to do Ciebie ( O NAS DLA WAS )",
"resourceId":{
"videoId":"Cc65GijorfM"
}
}
}
]
}
Teraz korzystając z narzędzia Plain Old Java Object, generujemy strukturę obiektów Java, a dokładniej mówiąc klasy, ponieważ obiekt jest reprezentacja klasy w programowaniu obiektowym.
Narzędzie JSON schema 2 POJO wygenerowało 5 obiektów: YouTubeResponse, Item, ResourceId oraz Snippet.
package pl.devtomek.domain;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"nextPageToken",
"items"
})
public class YouTubeResponse {
@JsonProperty("nextPageToken")
private String nextPageToken;
@JsonProperty("items")
private List<Item> items = null;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonProperty("nextPageToken")
public String getNextPageToken() {
return nextPageToken;
}
@JsonProperty("nextPageToken")
public void setNextPageToken(String nextPageToken) {
this.nextPageToken = nextPageToken;
}
@JsonProperty("items")
public List<Item> getItems() {
return items;
}
@JsonProperty("items")
public void setItems(List<Item> items) {
this.items = items;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
package pl.devtomek.domain;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"snippet"
})
public class Item {
@JsonProperty("snippet")
private Snippet snippet;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonProperty("snippet")
public Snippet getSnippet() {
return snippet;
}
@JsonProperty("snippet")
public void setSnippet(Snippet snippet) {
this.snippet = snippet;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
package pl.devtomek.domain;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"videoId"
})
public class ResourceId {
@JsonProperty("videoId")
private String videoId;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonProperty("videoId")
public String getVideoId() {
return videoId;
}
@JsonProperty("videoId")
public void setVideoId(String videoId) {
this.videoId = videoId;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
package pl.devtomek.domain;
import com.fasterxml.jackson.annotation.*;
import java.util.HashMap;
import java.util.Map;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"resourceId",
"title"
})
public class Snippet {
@JsonProperty("resourceId")
private ResourceId resourceId;
@JsonProperty("title")
private String title;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonProperty("resourceId")
public ResourceId getResourceId() {
return resourceId;
}
@JsonProperty("resourceId")
public void setResourceId(ResourceId resourceId) {
this.resourceId = resourceId;
}
@JsonProperty("title")
public String getTitle() {
return title;
}
@JsonProperty("title")
public void setTitle(String title) {
this.title = title;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Teraz możemy dokonać mapowania odpowiedź JSON na obiekt JAVA. W tym celu należy skorzystać z metody createParser() obiektu JsonFactory oraz readValues() obiektu ObjectMapper, dostępnych w pakiecie com.fasterxml.jackson.core.*. Poniżej zamieściłem przykładową strukturę metody, która generuje listę obiektów YouTubeResponse na podstawie odpowiedzi JSON.
public static List<YouTubeResponse> getYouTubeResponseList(String jsonResponse) throws IOException {
JsonFactory factory = new JsonFactory();
ObjectMapper mapper = new ObjectMapper();
JsonParser jsonParser = factory.createParser(jsonResponse);
MappingIterator<YouTubeResponse> youTubeResponse = mapper.readValues(jsonParser, YouTubeResponse.class);
List<YouTubeResponse> youTubeResponseList = youTubeResponse.readAll();
return youTubeResponseList;
}
Ostatecznie otrzymujemy obiekty, które mogą być dalej przetwarzane przez logikę naszego programu. Należy zwrócić uwagę, że metody rzucają wyjątek w momencie niepowodzenia podczas mapowania obiektu JSON na obiekt JAVA. Logika naszego programu musi to uwzględniać i odpowiednio reagować w takiej sytuacji, ponieważ w przeciwnym wypadku pojawi się wyjątek NullPointerException podczas próby odczytu pól obiektu.
Dodawanie zależności Maven
Aby mieć dostęp do obiektów znajdujących się w pakiecie Jackson musimy dodać odpowiednie zależności w naszym pliku pom.xml.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.6</version>
</dependency>
Teraz możemy już zaimportować niezbędne pakiety w naszej aplikacji. Przed skopiowaniem powyższych zależności warto sprawdzić, czy podane wersje nie są już przestarzałe.