Este é o segundo tutorial sobre JSON no R. Na primeira parte, trabalhamos com o pacote jsonlite
, que converte json para R. Neste, trabalhamos com o pacote jqr
, o qual manuseia o json por ele mesmo.
Diferentemente do jsonlite
, o jqr
opera com JSON como JSON mesmo. Ele é uma implementação da biblioteca jq: (http://stedolan.github.io/jq/) no R. Há duas interfaces, uma de baixo nível, que emula o jq
via linha de comando, ou de alto nível que adota funções do R. Primeiramente, mostraremos como o baixo nível porque ele é rico em recursos e uma vez que o conhece, você pode usá-lo inclusive na linha de comando.
Para instalar os binários no Mac e no Windows, basta chamar:
install.packages("jqr")
No Linux, você tem de instalar o biblioteca a libjq-dev
antes.
No baixo nível, você tem apenas de chamar a função jq
e colocar entre aspas aquilo que você escreveria se estivesse usando jq
no terminal. A biblioteca jq
tem um playground para você testar: (https://jqplay.org/).
Vamos retomar currículo criado no primeiro tutorial:
x <- '{
"id": 2,
"data": "2022-01-01",
"nome": {
"primeiro_nome": "José",
"sobrenome": "de Jesus Filho"
},
"cpf": "123.456.789-34",
"disponivel": true,
"educacao": [
{
"escola": "EMPG Milton Campos",
"ensino": "Fundamental",
"inicio": 1982,
"fim": 1989
},
{
"escola": "Derville Allegretti",
"ensino": "medio",
"inicio": 1990,
"fim": 1992
}
],
"experiencia_profissional": [
{
"empresa": "Companhia Brasileria de Distribuição",
"cargo": "empacotador",
"inicio": 1986,
"fim": 1988
},
{
"empresa": "Compneus",
"cargo": "gerente",
"inicio": 1990,
"fim": 1992
},
{
"empresa": "Varias",
"cargo": "muitos",
"inicio": 1992,
"fim": 2021
},
{
"empresa": "MPSP",
"cargo": "Jurimetrista",
"inicio": 2022,
"fim": null
}
]
}'
Suponha que você queira extrair do currículo acima o cpf. Basta chamar a função jq()
e colocar dentro de aspas o elemento precedido por ponto.
Você pode usar essa mesma sintaxe para obter subelementos.
x |>
jq(".nome.sobrenome")
"de Jesus Filho"
Se quiser obter o primeiro elemento do array educacao, use colchetes:
x |>
jq(".educacao[0]")
{
"escola": "EMPG Milton Campos",
"ensino": "Fundamental",
"inicio": 1982,
"fim": 1989
}
Note que JSON começa a contar do zero.
Se quiser a escola do segundo elemento de educacao:
x |>
jq(".educacao[1].escola")
"Derville Allegretti"
qr
:x |>
jq(".nome|.primeiro_nome")
"José"
E se você quiser algo que não existe:
x |>
jq(".idade")
null
A sintaxe acima funciona bem quando a chave (key) é simples, começa com uma letra e contêm somente dígitos e sublinhado. Isso aqui não funciona:
'{"1a": 4, "&b": 6}' |>
jq('.1a')
Error: jq: error: syntax error, unexpected IDENT, expecting $end (Unix shell quoting issues?) at <top-level>, line 1:
.1a
Mas se você colocar a chave entre colchetes, funciona porque esta é a forma segura:
'{"1a": 4, "&b": 6}' |>
jq('.["1a"]')
4
Se você separar os filtros por vírgula, ele roda cada um deles e retorna um array:
'{"primeiro_nome": "José","sobrenome": "de Jesus Filho"}' |>
jq('.primeiro_nome, .sobrenome')
[
"José",
"de Jesus Filho"
]
Se o seu JSON é um array, você pode fatiá-lo. No entanto, ele começa a contar do zero e, assim como em Python, o último índice não entra. Isso pode parecer estranho para usuários de R, mas com o tempo você se acostuma.
'["a", "b", "c", "d", "e"]' |>
jq(".[2:4]")
[
"c",
"d"
]
getpath
:x |>
jq('getpath(["nome", "primeiro_nome"], ["nome","sobrenome"])')
[
"José",
"de Jesus Filho"
]
Substituição é tão simples quanto. Vamo substituir “José” por “João” no primeiro_nome:
'{"primeiro_nome": "José","sobrenome": "de Jesus Filho"}' |>
jq('.primeiro_nome = "João"')
{
"primeiro_nome": "João",
"sobrenome": "de Jesus Filho"
}
Repare que precisei colocar tudo entre aspas simples porque primeiro_nome é texto e, por isso, precisei usar aspas duplas em “João”.
A função del
remove uma chave e seu correspondente valor:
'{"primeiro_nome": "José","sobrenome": "de Jesus Filho"}' |>
jq("del(.primeiro_nome)")
{
"sobrenome": "de Jesus Filho"
}
Você pode usar operadores para somar números:
'{"a": 3, "b": 5}' |>
jq(".a + 3")
6
'{"a": 3, "b": 5}' |>
jq(".a * .b")
15
Porém, quando se trata de texto:
'{"a": "arroz ", "b": "e ", "c": "feijão"}' |>
jq(".a + .b + .c")
"arroz e feijão"
Chaves na ordem em que aparecem:
x |>
jq(". | keys_unsorted")
[
"id",
"data",
"nome",
"cpf",
"disponivel",
"educacao",
"experiencia_profissional"
]
Chaves ordenadas alfabeticamente:
x |>
jq(". | keys")
[
"cpf",
"data",
"disponivel",
"educacao",
"experiencia_profissional",
"id",
"nome"
]
A chave existe?
x |>
jq('has("nome")')
true
Você usa map para operar em cada elemento e uma array:
'[2,6,8]' |>
jq("map(. + 1)")
[
3,
7,
9
]
Usa map_values
para objetos:
'{"a": 2, "b": 3, "c": 4}' |>
jq("map_values(. *4)")
{
"a": 8,
"b": 12,
"c": 16
}
Se você sabe expressões regulares, pode usar, mas ao usar “\”, você tem de dar escape duas vezes:
Verifica se no cpf há números:
x |>
jq('.cpf | test("\\\\d")')
true
Ou prefira raw strings:
arg <- r"(.experiencia_profissional[].empresa | test("(?i)com\\w+"))"
x |>
jq(arg)
[
true,
true,
false,
false
]
Você pode usar flags:
g - Pesquisa global (encontrar todas as correspondências, não apenas a primeira)
i - Não diferencia maiúsculas de minúsculas
m - Modo de várias linhas ('.' ignora quebra de linhas)
n - Ignora correspondências vazias
p - Ambos os modos s e m estão ativados
s - Modo de linha única ('^' -> '\A', '$' -> '\Z')
l - Encontre correspondências mais longas possíveis
x - Formato regex estendido (ignora espaços em branco e comentários)
x |>
jq('.nome.primeiro_nome | test("josé"; "i")')
true
Você pode extrair texto com base em regex usando capture
, mas você tem de informar a nova chave (key). Veja na sintaxe que eu começo com interrogação seguido da nova chave dentro de <…>:
'{"cep": ["05077-902", "02598154"]}' |>
jq('.cep[] | capture("(?<codigo_postal>[0-9]+-?[0-9]+)")')
[
{
"codigo_postal": "05077-902"
},
{
"codigo_postal": "02598154"
}
]
Outra função é match
, que retorna um objeto json com quatro campos:
offset: posição inicial do padrão length: tamanho do padrão string: o padrão encontrado captures: array de objectos com os grupos de captura. Estes, por sua vez, com os seguintes campos: offset, length, string, name. Abaixo não aparecem porque não usamos grupos de captura.
Vejamos:
arg <- r"(.educacao[].escola | match("\\w+ll\\w*"; "g"))"
x |>
jq(arg)
[
{
"offset": 0,
"length": 8,
"string": "Derville",
"captures": [
]
},
{
"offset": 9,
"length": 10,
"string": "Allegretti",
"captures": [
]
}
]
Se quiser preservar somente o padrão:
arg <- r"(.educacao[].escola | match("\\w+ll\\w*"; "g") | .string)"
x |>
jq(arg)
[
"Derville",
"Allegretti"
]
Este tutorial apenas tocou no pacote jqr
. Há uma uma multiplicidade de funções e recursos avançados os quais podem ser explorados sem sair do R. Aproveite para ler o manul: (https://stedolan.github.io/jq/manual)
For attribution, please cite this work as
Filho (2022, Dec. 26). Jurimetria: Json no R - Parte 2. Retrieved from https://direitoemdados.consudata.com.br/posts/2022-12-26-json-no-rparte2/
BibTeX citation
@misc{filho2022json, author = {Filho, José de Jesus}, title = {Jurimetria: Json no R - Parte 2}, url = {https://direitoemdados.consudata.com.br/posts/2022-12-26-json-no-rparte2/}, year = {2022} }