Json no R - Parte 2

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.

José de Jesus Filho https://github.com/jjesusfilho
2022-12-26

Pacote jqr

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.

Instalação

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.

Baixo nível

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
        }
    ]
}'

Extração

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.

library(jqr)

x |> 
  jq(".cpf")
"123.456.789-34"

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"

Com pipe do R:

x |> 
   jq(".nome") |> 
   jq(".primeiro_nome")
"José"

com pipe do qr:

x |> 
   jq(".nome|.primeiro_nome")
"José"

Objeto faltante

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

Vírgula

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"
]

Fatiamento

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"
]

Vários valores com getpath:

x |> 
  jq('getpath(["nome", "primeiro_nome"], ["nome","sobrenome"])')
[
    "José",
    "de Jesus Filho"
]

Substituição

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”.

Remoção de valores

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"
}

Operadores

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"

Somente as chaves

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

Operadores map e map_values

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
}

Expressões regulares

Se você sabe expressões regulares, pode usar, mas ao usar “\”, você tem de dar escape duas vezes:

Detecta

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

Captura

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"
    }
]

Match

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)

Citation

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}
}