DEV Community

Cover image for Manipulando arquivos JSON com Crystal
guto
guto

Posted on

Manipulando arquivos JSON com Crystal

Se você alguma vez já pensou em como seria trabalhar com a leitura de arquivos JSON utilizando Crystal, saiba que este é o lugar certo! Aqui veremos como podemos realizar manipulações simples de valores em JSON, trabalhando com variáveis, arrays, hashs, entre outras maneiras, manipulando nossas chaves com variáveis!

O que é JSON

O nome JSON vem de "JavaScript Object Notation", sendo um formato compacto de padrão aberto para troca e manipulação de dados simples, criado ainda nos anos 2000 (mais especificamente em 2002) utilizando um formato de chave-valor (atributo-valor).
Ok, mas, como seria o formato de um arquivo JSON?

[
    {
        "mensagem": "fala tropa"
    },
    {
        "resposta": "eai"
    }
]
Enter fullscreen mode Exit fullscreen mode

As chaves/atributos seriam os campos "mensagem" e "resposta", na qual seu valor está atribuído logo após dos dois pontos (":"), sendo separadas por chaves ("{ }") e vírgula indicando o próximo atributo completo.

Trabalhando com Crystal

Abrindo seu diretório de trabalho, crie um novo diretório para trabalharmos com este projeto, na qual vamos precisar de algumas "dependências" antes de iniciarmos:

  • Tenha certeza de ter o Crystal instalado corretamente na sua máquina
  • Prepare o editor de código favorito no diretório que foi criado
  • Caso não tenha instalado o Crystal ainda clique aqui para saber mais! Após abrir seu diretório de trabalho, caso queira iniciar o shards para criação de um arquivo para controle de projeto, execute em seu terminal:
$ shards init
Enter fullscreen mode Exit fullscreen mode

Seu arquivo shard.yml deverá seguir o seguinte formato:

name: json-reader

version: 0.1.0

authors:
    - João Lanjoni <guto@lanjoni.dev>

description: |
    JSON reader

targets:
    teste:
        main: src/main.cr

license: MIT
Enter fullscreen mode Exit fullscreen mode

Agora crie um diretório chamado src, nele vamos colocar todo o nosso código trabalhado utilizando Crystal! Crie também um diretório chamado content na raiz do projeto para conter todos os nossos arquivos em formato JSON!
Sendo assim, basta adicionar um arquivo no formato JSON com o nome que desejar no diretório content e adicionar um arquivo main.cr no diretório src, assim, ficaremos com nossa árvore:

json-reader/ 
    ├── content/ 
    │ └── index.json 
    ├── src/ 
    │ └── main.cr 
    └── shard.yml
Enter fullscreen mode Exit fullscreen mode

Lembrando que o shard.yml só existe se você iniciou o shards dentro de seu projeto!

Mãos na massa (no teclado)

Primeiramente vamos criar nosso arquivo JSON, por isso, seguindo o template passado acima crie um arquivo .json dentro do diretório content! Com ele vamos manipular os valores lá existentes!
Agora abra seu arquivo src/main.cr no seu editor de código favorito para podermos manipular melhor nosso projeto!
Abrindo seu arquivo primeiramente vamos importar a biblioteca json para trabalharmos com arquivos neste formato, assim, adicione em seu código:

require "json"
Enter fullscreen mode Exit fullscreen mode

Toda biblioteca pode ser adicionada com o comando require!

Para informarmos qual será o arquivo a ser lido, vamos realizar uma simples passagem por argumento/opção ao executar nosso projeto! Assim, caso queira se aprofundar um pouco mais sobre a passagem de argumentos na linha de comando com Crystal clique aqui. Dessa forma, vamos salvar o conteúdo do arquivo JSON em uma variável:

content = File.read("content/#{ARGV[0]}")
Enter fullscreen mode Exit fullscreen mode

Para recebermos o primeiro argumento/opção utilizaremos o ARGV[0], afinal, a posição 0 é a primeira no array de valores passados como opções! O content no começo significa que precisamos passar apenas o nome do arquivo, afinal, já será buscado um arquivo de mesmo nome no diretório especificado!

Certo, mas, existe ainda o caso do usuário não adicionar nenhuma opção, certo? Neste caso o código nem deve continuar, afinal, se um arquivo JSON não for especificado, logo, não podemos realizar a manipulação! Assim, antes, adicionamos uma simples verificação:

if ARGV.size != 1
    puts "É necessário passar o nome do arquivo como parâmetro!"
    exit 1
end
Enter fullscreen mode Exit fullscreen mode

O tamanho sendo diferente de 1 demonstra que foram passados ou mais de um argumento ou nenhum, por isso, em ambos os casos, devemos rejeitar a execução!

Para realizar o parse de nosso JSON existem algumas formas, vamos começar com a mais simples: JSON de chaves únicas! Veja o JSON de exemplo abaixo:

{
    "teste": "oi",
    "teste2": "tchau"
}
Enter fullscreen mode Exit fullscreen mode

Perceba que este formato não possui colchetes em seu início, indicando não ser um array!

Para realizar o parse vamos utilizar uma função nativa do Crystal para realizar a leitura de um JSON e depois convertê-lo em um Hash de Strings!

hash_content = Hash(String, String).from_json(content)

puts hash_content # {"teste" => "oi", "teste2" => "tchau"}
Enter fullscreen mode Exit fullscreen mode

Desta forma vamos montar uma variável com nome hash_content, tendo em seu conteúdo um hash com chave valor de String para String, trazendo estes dados de um JSON

Logo, nosso código final será algo como:

require "json"

# Verificação de argumentos sendo igual a 1
if ARGV.size != 1
    puts "É necessário passar o nome do arquivo como parâmetro!"
    exit 1
end

# Carregando o conteúdo do arquivo JSON
content = File.read("content/#{ARGV[0]}")  

# Transformando os dados do arquivo JSON em um Hash de String => String
hash_content = Hash(String, String).from_json(content)

# Imprimindo o conteúdo da variável
puts hash_content # {"teste" => "oi", "teste2" => "tchau"}
Enter fullscreen mode Exit fullscreen mode

Podemos executar de duas formas:

  • Utilizando shards:


    $ shards run -- content.json

  • Utilizando o próprio crystal:


    $ crystal run src/main.cr content.json

Certo, mas, e se quiséssemos apenas o retorno da chave teste?

puts hash_content["teste"] # oi
Enter fullscreen mode Exit fullscreen mode

Assim, conseguimos manipular nossos dados no arquivo JSON, podendo trazer as chaves específicas para cada item!

Arrays de JSONs

Certo, mas, a maioria dos JSONs que vamos encontrar precisam ser "parseados" utilizando formatos de Array... Como podemos fazer isso? Bom, existem duas formas específicas para realizarmos essa tarefa, bora lá?

Primeira forma: Array de Hash

Basicamente vamos montar um array com seu tipo interno sendo hash! Mas, como isso ficaria na prática? Bom, primeiro vou repassar agora o arquivo JSON que estaremos trabalhando:

[
    {
        "teste": "fala tropa"
    },
    {
        "teste2": "eai"
    }
]
Enter fullscreen mode Exit fullscreen mode

Vamos primeiramente definir que estamos trabalhando com um array, mas, no momento de definir o tipo do array vamos trocar para Hash(String, String), veja:

json = Array(Hash(String, String)).from_json(content)

puts json # [{"teste" => "fala tropa"}, {"teste2" => "eai"}]
Enter fullscreen mode Exit fullscreen mode

Perceba que basicamente envolvemos o formato de hash anterior no array, correto? Assim podemos trabalhar com nossos arrays de JSONs da forma que preferirmos! Mas, como eu poderia trazer apenas o conteúdo da chave "teste"? Simples, veja dois exemplos abaixo:

puts json[0] # {"teste" => "fala tropa"}

puts json[0]["teste"] # fala tropa
Enter fullscreen mode Exit fullscreen mode

Assim, conseguimos manipular nosso JSON!

Caso o formato de chave não seja sempre String, podemos ainda criar um alias para manipular os tipos passados por Hash ou então apenas adicionarmos um operador pipe ("|") demonstrando que o tipo pode variar, sendo assim seria algo como:

Hash(String | Float64, String | Int32)
Enter fullscreen mode Exit fullscreen mode

Segunda forma: File.open

Podemos ainda utilizar o File.open para abrir um arquivo e trabalhar com seu conteúdo (na qual deixo os créditos para a cherry por ter passado na live e deixado esse detalhe importantíssimo), sem a necessidade da criação de uma variável para realizar esse trabalho (como estávamos fazendo com a variável content). Veja uma implementação abaixo:

json = File.open("content/#{ARGV[0]}") do |file|
    JSON.parse(file)
end

puts json # [{"teste" => "fala tropa"}, {"teste2" => "eai"}]

puts json[0] # {"teste" => "fala tropa"}

puts json[0]["teste"] # fala tropa
Enter fullscreen mode Exit fullscreen mode

Neste exemplo realizamos um simples "parse" de valores e atualizamos na variável de nome json, podendo trabalhar com os valores no mesmo formato que anteriormente!

Assim, nosso projeto completo finaliza na segunda forma! Veja como ficou nosso código finalizado:

require "json"

if ARGV.size != 1
    puts "É necessário passar o nome do arquivo como parâmetro!"
    exit 1
end

json = File.open("content/#{ARGV[0]}") do |file|
    JSON.parse(file)
end

puts json # [{"teste" => "fala tropa"}, {"teste2" => "eai"}]
Enter fullscreen mode Exit fullscreen mode

Finalização

Com este guia e pequeno artigo você aprendeu a como manipular arquivos JSON e dados vindo de outros arquivos utilizando Crystal, baseando-se no modelo chave-valor que é oferecido, podendo criar comandos, leituras específicas, entre outros tipos de projetos! Para acessar o código desenvolvido neste artigo basta clicar aqui!
Nos vemos na próxima, até mais! Cristalize ainda mais o seu dia! 💎 🖤

Top comments (2)

Collapse
 
cherryramatis profile image
Cherry Ramatis

Bom demais! Curti muito a capacidade de tipar pelo from_json, fica super simples de garantir segurança com tipos

Collapse
 
soso profile image
Sofia Borges Vidal

Conteúdo incrível! Espero ver mais dele por aqui!!!