Rusty Full Stack
El blog para los amantes de Rust, Ionic y Vuejs
El blog para los amantes de Rust, Ionic y Vuejs
En post anteriores, hemos convertido un JSON a una estructura en Rust utilizando serde_json y serde. Esta vez realizaremos la operación en el otro sentido y convertiremos una estructura en un archivo JSON.
Te preguntarás por qué podría interesarnos el realizar esta operación, un caso de uso bastante común es transferir nuestros datos utilizando un API que muchas veces utiliza JSON como contenido aceptado para la transferencia.
En nuestra mini serie sobre Json, serde y serde_json, hemos utilizado un formato JSON para representar un libro y lo hemos transformado a una estructura de tipo "Libro" para poder imprimir en nuestra consola (Convertir un String JSON a Estructura con Rust), esta vez vamos a asumir no estamos generando nuestro objeto de tipo Libro desde un Json String, sino, que lo estamos generando de cualquier otra forma y debemos transformar nuestro objeto a un JSON vamos transmitirlo a un API.
El Json resultante debe verse similar a este formato:
// JSON esperado:
{
"titulo": "The Pragmatic Programmer",
"autores": [
"David Thomas",
"Andrew Hunt"
],
"total_paginas": 352,
"generos": [
"programacion",
"ingenieria",
"educacion"
],
"precios": [
{
"tipo": "digital",
"precio": 15.00,
"moneda": "USD"
},
{
"tipo": "tapa dura",
"precio": 35.50,
"moneda": "USD"
}
]
}
En nuestro ejemplo, vamos a crear un pequeño programa en el cual vamos a realizar las siguientes actividades.
Empecemos creando nuestro proyecto ejecutando en nuestra terminal el comando de cargo, si quieres ver el código completo puedes hacerlo dando click a este enlace.
cargo new estructura-a-json
cd estructura-a-json
Como hemos mencionado, el primer paso será el incorporar serde y serde_json, hay que recordar que debemos activar los features del macro derive para serde, esto nos permitirá agregar el macro par serializar y deserializar el objeto. Nuestro archivo Cargo.toml debe verse similar al siguiente:
// Cargo.toml
[package]
name = "estructura-a-json"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde_json = "1.0.82"
serde = { version = "1.0.138", features = ["derive"] }
Con los crates necesarios incluidos, ahora vamos a agregarlos en nuestro archivo main.rs
src/main.rs
use serde_json;
use serde::{Serialize, Deserialize};
Ahora vamos a agregar nuestras estructuras.
Disclaimer: Si quieres leer una explicación más de detalla sobre la estructura y los use de serde y serde_json, puedes ver el post anterior "Convertir un String JSON a Estructura con Rust" 😉
Las estructuras siempre las vamos a incluir en nuestro archivo main.rs y debe verse similar al siguiente código:
// src/main.rs
use serde_json;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Libro {
titulo: String,
total_paginas: u32,
autores: Vec<String>,
generos: Vec<String>,
precios: Vec<Precio>
}
#[derive(Serialize, Deserialize, Debug)]
struct Precio {
precio: f32,
tipo: String,
moneda: String,
}
En síntesis, nuestro libro tiene sus propios atributos y uno especial para definir sus precios dependiendo de su tipo (digital, tapa dura, etc).
Ahora creemos una función main en la cual creemos un objeto libro.
// src/main.rs
use serde_json;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Libro {
titulo: String,
total_paginas: u32,
autores: Vec<String>,
generos: Vec<String>,
precios: Vec<Precio>
}
#[derive(Serialize, Deserialize, Debug)]
struct Precio {
precio: f32,
tipo: String,
moneda: String,
}
fn main() {
let libro = Libro {
titulo: String::from("The Pragmatic Programmer"),
total_paginas: 320,
autores: vec![
String::from("David Thomas"),
String::from("Andrew Hunt")
],
generos: vec![
String::from("Programming"),
String::from("IT")
],
precios: vec![
Precio {
precio: 35.00,
tipo: String::from("Tapa Dura"),
moneda: String::from("USD")
},
Precio {
precio: 15.00,
tipo: String::from("digital"),
moneda: String::from("USD")
}
]
};
println!("{:?}", libro);
}
En el código anterior, estamos construyendo un objeto de tipo libro de la forma nativa es que son creados los objetos en Rust (sin utilizar constructores o builders), no hay que confundir este método de construcción con un Json, pues son bastante parecidos.
Si ejecutamos nuestro programa vamos a ver el siguiente resultado (ignora el warning por el momento, se va a resolver cuando empecemos a utilizar serde_json)
cargo run
El resultado es:
Al ver el print del objeto también hay que tener cuidado porque parece un JSON, pero no lo es en realidad, es únicamente la representación en String del objeto de tipo Libro, la cual se logra gracias al macro derive Debug que hemos colocado al struct Libro y Precio. También es de mencionar que para imprimir la estructura con el macro derive Debug, es importante que el println! incluya {:?}.
Bien, ahora que sabemos que lo que nuestro programa está imprimiendo en pantalla no es un JSON, entonces vamos a generarlo, ahi es donde entra serde_json.
Hagamos una pequeña modificación en nuestro programa de la siguiente forma:
// src/main.rs
use serde_json;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Libro {
titulo: String,
total_paginas: u32,
autores: Vec<String>,
generos: Vec<String>,
precios: Vec<Precio>
}
#[derive(Serialize, Deserialize, Debug)]
struct Precio {
precio: f32,
tipo: String,
moneda: String,
}
fn main() {
let libro = Libro {
titulo: String::from("The Pragmatic Programmer"),
total_paginas: 320,
autores: vec![
String::from("David Thomas"),
String::from("Andrew Hunt")
],
generos: vec![
String::from("Programming"),
String::from("IT")
],
precios: vec![
Precio {
precio: 35.00,
tipo: String::from("Tapa Dura"),
moneda: String::from("USD")
},
Precio {
precio: 15.00,
tipo: String::from("digital"),
moneda: String::from("USD")
}
]
};
// Importante, en este caso como tenemos el control, podemos
// utilizar directamente el unwrap, en caso contrario debemos controlar
// el error, pero por sencillez no lo haremos en este ejemplo.
let libro_convertido_a_json = serde_json::to_string(&libro).unwrap();
println!("{}", libro_convertido_a_json);
}
Si ejecutamos el código anterior (verás que el warning ya no aparece 😎)
cargo run
El programa ahora SI imprime un String en formato JSON.
serde_json se encarga de transformar nuestra estructura con esta línea:
let libro_convertido_a_json = serde_json::to_string(&libro).unwrap();
Debes notar dos cosas, la primera es que estamos utilizando el unwrap, esto debido a que tenemos el control y sabemos que nuestra objeto de tipo Libro, siempre será valido, en otros casos no olvides controlar el error (si quieres aprender a hacerlo escríbelo en los comentarios 😏)
Lo segundo que debes notar es que el objeto libro se está pasando utilizando el &, lo que significa que estamos pasando el objeto por referencia, es decir su ubicación en la memoria, esto ayuda a nivel de memoria puesto que no pasamos totalmente el objeto a la función.
Si quieres el código completo de nuestro ejemplo, puedes hacerlo desde nuestro repositorio en github dando click a este enlace.
Con este ejemplo hemos terminado nuestra mini serie sobre Json con Rust, pero ojo, no quiere decir que no existan muchas cosas más por explorar sobre serde y serde_json, te aconsejo le puedas echar un vistazo a la documentación para conocer más detalles sobre todo lo que se puede hacer con estos dos super útiles crates, si quieres que exploremos algún tema déjalo en los comentarios.
Si este post te ha sido de utilidad, por favor, compártelo con tus amigos y en tus redes sociales.
println!("Hasta la próxima!!");