Processamento JSON no Java EE 7
1. Visão geral
Este artigo mostra como processar o JSON usando apenas o Java EE principal, sem o uso de dependências de terceiros como Jersey ou Jackson. Quase tudo que usaremos é fornecido pelo pacotejavax.json.
2. Gravando um objeto em JSONString
Converter um objeto Java em JSONString é muito fácil. Vamos supor que temos uma classePerson simples:
public class Person {
private String firstName;
private String lastName;
private Date birthdate;
// getters and setters
}
Para converter uma instância dessa classe em JSONString, primeiro precisamos criar uma instância deJsonObjectBuildere adicionar pares propriedade / valor usando o métodoadd():
JsonObjectBuilder objectBuilder = Json.createObjectBuilder()
.add("firstName", person.getFirstName())
.add("lastName", person.getLastName())
.add("birthdate", new SimpleDateFormat("DD/MM/YYYY")
.format(person.getBirthdate()));
Observe que o métodoadd() tem algumas versões sobrecarregadas. Ele pode receber a maioria dos tipos primitivos (assim como objetos em caixa) como seu segundo parâmetro.
Depois de definir as propriedades, só precisamos escrever o objeto em umString:
JsonObject jsonObject = objectBuilder.build();
String jsonString;
try(Writer writer = new StringWriter()) {
Json.createWriter(writer).write(jsonObject);
jsonString = writer.toString();
}
E é isso! OString gerado será semelhante a este:
{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978"}
2.1. UsandoJsonArrayBuilder para construir matrizes
Agora, para adicionar um pouco mais de complexidade ao nosso exemplo, vamos supor que a classePerson foi modificada para adicionar uma nova propriedade chamadaemails que conterá uma lista de endereços de e-mail:
public class Person {
private String firstName;
private String lastName;
private Date birthdate;
private List emails;
// getters and setters
}
Para adicionar todos os valores dessa lista aJsonObjectBuilder, precisaremos da ajuda deJsonArrayBuilder:
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
for(String email : person.getEmails()) {
arrayBuilder.add(email);
}
objectBuilder.add("emails", arrayBuilder);
Observe que estamos usando outra versão sobrecarregada do métodoadd() que leva um objetoJsonArrayBuilder como seu segundo parâmetro.
Então, vamos dar uma olhada na String gerada para um objetoPerson com dois endereços de e-mail:
{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978",
"emails":["[email protected]","[email protected]"]}
2.2. Formatando a saída comPRETTY_PRINTING
Portanto, convertemos com sucesso um objeto Java em um JSONString válido. Agora, antes de passar para a próxima seção, vamos adicionar alguma formatação simples para tornar a saída mais "semelhante a JSON" e mais fácil de ler.
Nos exemplos anteriores, criamos umJsonWriter usando o método estáticoJson.createWriter() direto. A fim de obter mais controle dosString gerados, aproveitaremos a capacidade deJsonWriterFactory do Java 7 para criar um gravador com uma configuração específica.
Map config = new HashMap<>();
config.put(JsonGenerator.PRETTY_PRINTING, true);
JsonWriterFactory writerFactory = Json.createWriterFactory(config);
String jsonString;
try(Writer writer = new StringWriter()) {
writerFactory.createWriter(writer).write(jsonObject);
jsonString = writer.toString();
}
O código pode parecer um pouco prolixo, mas realmente não faz muito.
Primeiro, ele cria uma instância deJsonWriterFactory passando um mapa de configuração para seu construtor. O mapa contém apenas uma entrada que é verdadeira para a propriedade PRETTY_PRINTING. Em seguida, usamos essa instância de fábrica para criar um escritor, em vez de usarJson.createWriter().
A nova saída conterá as quebras de linha e tabulação distintas que caracterizam um JSONString:
{
"firstName":"Michael",
"lastName":"Scott",
"birthdate":"06/15/1978",
"emails":[
"[email protected]",
"[email protected]"
]
}
3. Construindo um JavaObject de umString
Agora vamos fazer a operação oposta: converter um JSONString em um objeto Java.
A parte principal do processo de conversão gira em torno deJsonObject. Para criar uma instância desta classe, use o método estáticoJson.createReader() seguido porreadObject():
JsonReader reader = Json.createReader(new StringReader(jsonString));
JsonObject jsonObject = reader.readObject();
O métodocreateReader() usaInputStream como parâmetro. Neste exemplo, estamos usandoStringReader,, pois nosso JSON está contido em um objetoString, mas esse mesmo método pode ser usado para ler o conteúdo de um arquivo, por exemplo, usandoFileInputStream.
Com uma instância deJsonObject em mãos, podemos ler as propriedades usando o métodogetString() e atribuir os valores obtidos a uma instância recém-criada de nossa classePerson:
Person person = new Person();
person.setFirstName(jsonObject.getString("firstName"));
person.setLastName(jsonObject.getString("lastName"));
person.setBirthdate(dateFormat.parse(jsonObject.getString("birthdate")));
3.1. UsandoJsonArray para obter valores deList
Precisaremos usar uma classe especial, chamadaJsonArray para extrair os valores da lista deJsonObject:
JsonArray emailsJson = jsonObject.getJsonArray("emails");
List emails = new ArrayList<>();
for (JsonString j : emailsJson.getValuesAs(JsonString.class)) {
emails.add(j.getString());
}
person.setEmails(emails);
É isso aí! Criamos uma instância completa dePerson a partir de um JsonString.
4. Consultando Valores
Agora, vamos supor que estejamos interessados em um dado muito específico que está dentro de um JSONString.
Considere o JSON abaixo representando um cliente de uma loja de animais. Digamos que, por algum motivo, você precise obter o nome do terceiro animal da lista de animais de estimação:
{
"ownerName": "Robert",
"pets": [{
"name": "Kitty",
"type": "cat"
}, {
"name": "Rex",
"type": "dog"
}, {
"name": "Jake",
"type": "dog"
}]
}
Converter todo o texto em um objeto Java apenas para obter um único valor não seria muito eficiente. Então, vamos verificar algumas estratégias para consultar JSONStrings sem ter que passar por toda a provação de conversão.
4.1. Consultando usando a API de modelo de objeto
Consultar o valor de uma propriedade com um local conhecido na estrutura JSON é simples. Podemos usar uma instância deJsonObject, da mesma classe usada nos exemplos anteriores:
JsonReader reader = Json.createReader(new StringReader(jsonString));
JsonObject jsonObject = reader.readObject();
String searchResult = jsonObject
.getJsonArray("pets")
.getJsonObject(2)
.getString("name");
O problema aqui é navegar pelas propriedadesjsonObject usando a sequência correta dos métodosget*().
Neste exemplo, primeiro obtemos uma referência à lista de "animais de estimação" usando o métodogetJsonArray(),_ which returns a list with 3 records. Then, we use _getJsonObject(), que recebe um índice como parâmetro, retornando outroJsonObject representando o terceiro item no Lista. Finalmente, usamosgetString() para obter o valor da string que estamos procurando.
4.2. Consultando usando a API de streaming
Outra maneira de realizar consultas precisas em JSONString é usando a API de streaming, que temJsonParser como classe principal.
JsonParser fornece acesso direto, somente leitura e extremamente rápido ao JS, com a desvantagem de ser um pouco mais complicado do que o modelo de objeto:
JsonParser jsonParser = Json.createParser(new StringReader(jsonString));
int count = 0;
String result = null;
while(jsonParser.hasNext()) {
Event e = jsonParser.next();
if (e == Event.KEY_NAME) {
if(jsonParser.getString().equals("name")) {
jsonParser.next();
if(++count == 3) {
result = jsonParser.getString();
break;
}
}
}
}
Este exemplo fornece o mesmo resultado que o anterior. Ele retornaname do terceiro animal de estimação na listapets.
Depois que umJsonParser é criado usandoJson.createParser(), precisamos usar um iterador (daí a natureza de “acesso direto” deJsonParser) para navegar pelos tokens JSON até chegarmos à propriedade (ou propriedades) que estamos procurando.
Toda vez que percorremos o iterador, passamos para o próximo token dos dados JSON. Portanto, temos que ter cuidado para verificar se o token atual tem o tipo esperado. Isso é feito verificando oEvent retornado pela chamadanext().
Existem muitos tipos diferentes de tokens. Neste exemplo, estamos interessados nos tiposKEY_NAME, que representam o nome de uma propriedade (por exemplo “OwnerName”, “pets”, “name”, “type”). Depois de percorrermos um tokenKEY_NAME com um valor de “nome” pela terceira vez, sabemos que o próximo token conterá um valor de string representando o nome do terceiro animal de estimação da lista.
Definitivamente, é mais difícil do que usar a API de modelo de objeto, especialmente para estruturas JSON mais complicadas. A escolha entre um ou outro, como sempre, depende do cenário específico com o qual você estará lidando.
5. Conclusão
Abordamos muito sobre a API de processamento Java EE JSON com alguns exemplos simples. Para aprender outras coisas interessantes sobre o processamento JSON, verifique nossoseries of Jackson articles.
Verifique o código-fonte das classes usadas neste artigo, bem como alguns testes de unidade, em nossoGitHub repository.