Este desafio foi um tanto quanto difícil por ser a minha segunda vez mexendo com esse sistema de consumir APIs. Esta é a minha segunda tentativa, agora usando a API do Google Books, e nela utilizei o Gson em vez do Jackson.
E nossa, como foi complicado de entender! O código eu consegui escrever de boa, mas na hora dos testes estava sempre me retornando um NPE. Foi aí que entendi que, para trabalhar com isso, é preciso ir fazendo aos poucos, em vez de tentar resolver tudo de uma vez e depois sofrer para corrigir bugs, como eu sofri.
Contudo, isso me fez aprender bastante. Uma coisa, por exemplo, é sempre testar primeiro o sistema de JSON para verificar se está tudo certo, porque nele você sempre consegue tirar boas dicas. Suas classes, que irão serializar o JSON, precisam ter o mesmo escopo em termos de nomes e abstrações. Era exatamente isso que estava me causando tanta dificuldade, pois sempre me retornava um NPE por conta dos nomes errôneos postos nos atributos do objeto
Class Main
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
HttpClientProvider client = HttpClientProvider.create();
System.out.println("Enter title:");
String title = scanner.nextLine().trim();
String url = BookService.build(title);
String json = client.getBody(url);
BookResponse response = BookParser.parse(json, BookResponse.class);
if (response.getItems().isEmpty()) {
System.out.println("No books found");
return;
}
response.getItems().forEach(item -> {
System.out.printf("Title: %s%n", item.getVolumeInfo().getTitle());
System.out.printf("Authors: %s%n", Arrays.toString(item.getVolumeInfo().getAuthors()));
System.out.printf("Publisher: %s%n", item.getVolumeInfo().getPublisher());
System.out.printf("Description: %s%n", item.getVolumeInfo().getDescription());
});
scanner.close();
}
}
Class BookParser
@lombok.experimental.UtilityClass
public class BookParser {
private final Gson gson = getGson();
public <T> T parse(String json, Class<T> clazz) {
if (json == null) return null;
try {
return gson.fromJson(json, clazz);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Contract(" -> new")
@ApiStatus.Internal
private @NotNull Gson getGson() {
return new GsonBuilder().create();
}
}
Class BookService
public class BookService {
@Contract("null -> null; !null -> !null")
public static String build(String title) {
if (title == null) return null;
return "https://www.googleapis.com/books/v1/volumes?q="
+ StringUtil.replace(title)
+ "&key="
+ SecretKey.KEY.getKey();
}
}
Class StringUtil
@lombok.experimental.UtilityClass
public class StringUtil {
public String replace(String title) {
if (title == null) return null;
return title.replace(" ", "%20");
}
}
Record HttpClientProvider
public record HttpClientProvider(@lombok.Getter(value = lombok.AccessLevel.PUBLIC) HttpClient client) {
@Contract(" -> new")
public static @NotNull HttpClientProvider create() {
return new HttpClientProvider(HttpClient.newHttpClient());
}
public String getBody(String url) {
HttpResponse<String> response = null;
try {
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
response = client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
e.printStackTrace();
}
assert response != null;
return response.body();
}
}
Class BookResponse
@lombok.Data
public class BookResponse {
List<Item> items;
}
Class VolumeInfo
@lombok.Data
public class VolumeInfo {
private String title;
private String[] authors;
private String publisher;
private String description;
}
Class Item
@lombok.Data
public class Item {
private VolumeInfo volumeInfo;
}
Class Book
@lombok.ToString(of = {"title", "authors", "publisher", "description"})
@lombok.AllArgsConstructor(access = lombok.AccessLevel.PUBLIC)
@lombok.NoArgsConstructor(access = lombok.AccessLevel.PUBLIC)
@lombok.Getter(value = lombok.AccessLevel.PUBLIC)
public class Book {
@SerializedName("title")
private String title;
@SerializedName("authors")
private String[] authors;
@SerializedName("publisher")
private String publisher;
@SerializedName("description")
private String description;
}