1032 lines
24 KiB
Markdown
1032 lines
24 KiB
Markdown
# serde a serde_json — krok za krokom
|
|
|
|
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
|
|
|
|
`Cargo.toml`:
|
|
```toml
|
|
[dependencies]
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
```
|
|
|
|
```rust
|
|
use serde::{Serialize, Deserialize};
|
|
```
|
|
|
|
---
|
|
|
|
## Kapitola 1: Čo je serde
|
|
|
|
serde je framework na **serializáciu** a **deserializáciu** dát v Ruste.
|
|
|
|
- **Serializácia** = Rust štruktúra → nejaký formát (JSON, TOML, YAML...)
|
|
- **Deserializácia** = nejaký formát → Rust štruktúra
|
|
|
|
serde samotné nerobí nič s JSON. Je to len systém traitov. Konkrétny formát zabezpečuje knižnica ako `serde_json`.
|
|
|
|
```
|
|
Rust štruktúra ──Serialize──→ JSON string
|
|
JSON string ──Deserialize──→ Rust štruktúra
|
|
```
|
|
|
|
serde funguje cez dva traity:
|
|
- `Serialize` — štruktúra sa vie premeniť na dáta
|
|
- `Deserialize` — dáta sa vedia premeniť na štruktúru
|
|
|
|
Neimplementuješ ich ručne. Použiješ `#[derive()]`:
|
|
|
|
```rust
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Osoba {
|
|
meno: String,
|
|
vek: u32,
|
|
}
|
|
```
|
|
|
|
Toto je všetko. Teraz Osoba vie byť serializovaná a deserializovaná.
|
|
|
|
### Úlohy 1
|
|
|
|
**1a.** Čo je serializácia? Čo je deserializácia? Povedz to vlastnými slovami.
|
|
|
|
**1b.** Aký je rozdiel medzi `serde` a `serde_json`?
|
|
|
|
**1c.** Napíš štruktúru `Kniha` s poľami `nazov: String`, `autor: String`, `rok: u16`. Pridaj derive pre Serialize a Deserialize.
|
|
|
|
---
|
|
|
|
## Kapitola 2: Serialize — štruktúra → JSON
|
|
|
|
```rust
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Osoba {
|
|
meno: String,
|
|
vek: u32,
|
|
email: String,
|
|
}
|
|
|
|
fn main() {
|
|
let anna = Osoba {
|
|
meno: "Anna".to_string(),
|
|
vek: 25,
|
|
email: "anna@mail.sk".to_string(),
|
|
};
|
|
|
|
// to_string — kompaktný JSON (jeden riadok)
|
|
let json: String = serde_json::to_string(&anna).unwrap();
|
|
println!("{}", json);
|
|
// {"meno":"Anna","vek":25,"email":"anna@mail.sk"}
|
|
|
|
// to_string_pretty — pekne formátovaný JSON
|
|
let json_pretty: String = serde_json::to_string_pretty(&anna).unwrap();
|
|
println!("{}", json_pretty);
|
|
// {
|
|
// "meno": "Anna",
|
|
// "vek": 25,
|
|
// "email": "anna@mail.sk"
|
|
// }
|
|
}
|
|
```
|
|
|
|
**Návratový typ:** `serde_json::to_string` vracia `Result<String, serde_json::Error>`. Preto `.unwrap()` alebo `?` alebo `.ok()`.
|
|
|
|
**Čo sa serializuje:**
|
|
- `String` → `"text"`
|
|
- čísla → čísla
|
|
- `bool` → `true`/`false`
|
|
- `Vec<T>` → `[...]`
|
|
- `Option<T>` → hodnota alebo `null`
|
|
- `HashMap<String, T>` → `{...}` objekt
|
|
- vnorené štruktúry → vnorené objekty
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Kniznica {
|
|
nazov: String,
|
|
knihy: Vec<Kniha>, // → JSON pole
|
|
otvorena: bool, // → true/false
|
|
poznamka: Option<String>, // → "text" alebo null
|
|
}
|
|
```
|
|
|
|
### Úlohy 2
|
|
|
|
**2a.** Vytvor štruktúru `Auto` s poľami `znacka: String`, `rok: u16`, `cena: f64`. Serializuj jedno auto do JSON. Vypíš kompaktne aj pretty.
|
|
|
|
**2b.** Vytvor vektor 3 áut. Serializuj celý vektor do JSON. Aký formát má výstup?
|
|
|
|
**2c.** Vytvor štruktúru `Garaz` s poľom `auta: Vec<Auto>`. Serializuj garáž s 2 autami. Ako vyzerá JSON?
|
|
|
|
**2d.** Čo sa stane ak pole je `Option<String>` a je `None`? Serializuj a pozri.
|
|
|
|
---
|
|
|
|
## Kapitola 3: Deserialize — JSON → štruktúra
|
|
|
|
```rust
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
struct Osoba {
|
|
meno: String,
|
|
vek: u32,
|
|
}
|
|
|
|
fn main() {
|
|
let json = r#"{"meno":"Anna","vek":25}"#;
|
|
|
|
// from_str — JSON string → štruktúra
|
|
let osoba: Osoba = serde_json::from_str(json).unwrap();
|
|
println!("{:?}", osoba);
|
|
// Osoba { meno: "Anna", vek: 25 }
|
|
|
|
// Typ musíš uviesť (Rust nevie aká štruktúra)
|
|
let osoba: Result<Osoba, _> = serde_json::from_str(json);
|
|
match osoba {
|
|
Ok(o) => println!("Meno: {}", o.meno),
|
|
Err(e) => println!("Chyba: {}", e),
|
|
}
|
|
}
|
|
```
|
|
|
|
**`r#"..."#`** — raw string literal. Netreba escapovať úvodzovky. Veľmi užitočné pre JSON.
|
|
|
|
**Čo sa stane ak JSON nesedí?**
|
|
|
|
```rust
|
|
// Chýba pole
|
|
let json = r#"{"meno":"Anna"}"#;
|
|
let vysledok: Result<Osoba, _> = serde_json::from_str(json);
|
|
// Err — chýba pole "vek"
|
|
|
|
// Zlý typ
|
|
let json = r#"{"meno":"Anna","vek":"dvadsatpat"}"#;
|
|
let vysledok: Result<Osoba, _> = serde_json::from_str(json);
|
|
// Err — vek nie je číslo
|
|
|
|
// Extra pole — OK! serde ho ignoruje
|
|
let json = r#"{"meno":"Anna","vek":25,"extra":"niečo"}"#;
|
|
let osoba: Osoba = serde_json::from_str(json).unwrap();
|
|
// OK — extra pole sa ignoruje
|
|
```
|
|
|
|
### Úlohy 3
|
|
|
|
**3a.** Máš JSON:
|
|
```json
|
|
{"nazov":"Duna","autor":"Frank Herbert","rok":1965}
|
|
```
|
|
Deserializuj ho do štruktúry `Kniha`. Vypíš názov a rok.
|
|
|
|
**3b.** Máš JSON pole:
|
|
```json
|
|
[{"nazov":"A","rok":2000},{"nazov":"B","rok":2010}]
|
|
```
|
|
Deserializuj ho do `Vec<Kniha>`. Vypíš počet kníh.
|
|
|
|
**3c.** Čo sa stane ak JSON obsahuje pole, ktoré štruktúra nemá? Vyskúšaj.
|
|
|
|
**3d.** Čo sa stane ak JSON-u chýba pole, ktoré štruktúra vyžaduje? Vyskúšaj.
|
|
|
|
---
|
|
|
|
## Kapitola 4: Enumy v serde
|
|
|
|
```rust
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
// Jednoduchý enum (bez dát) — serializuje sa ako string
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
enum Farba {
|
|
Cervena,
|
|
Modra,
|
|
Zelena,
|
|
}
|
|
|
|
let json = serde_json::to_string(&Farba::Cervena).unwrap();
|
|
// "Cervena"
|
|
|
|
let farba: Farba = serde_json::from_str(r#""Modra""#).unwrap();
|
|
// Farba::Modra
|
|
```
|
|
|
|
```rust
|
|
// Enum s dátami — komplexnejší JSON
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
enum Sprava {
|
|
Text(String),
|
|
Cislo(i32),
|
|
Pozicia { x: f64, y: f64 },
|
|
Prazdna,
|
|
}
|
|
|
|
let json = serde_json::to_string(&Sprava::Text("ahoj".into())).unwrap();
|
|
// {"Text":"ahoj"}
|
|
|
|
let json = serde_json::to_string(&Sprava::Pozicia { x: 1.0, y: 2.0 }).unwrap();
|
|
// {"Pozicia":{"x":1.0,"y":2.0}}
|
|
|
|
let json = serde_json::to_string(&Sprava::Prazdna).unwrap();
|
|
// "Prazdna"
|
|
```
|
|
|
|
Jednoduchý enum (bez dát) v štruktúre:
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
enum Stav {
|
|
Nova,
|
|
Pouzivana,
|
|
Poskodena,
|
|
Vyradena,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
struct Kniha {
|
|
nazov: String,
|
|
stav: Stav,
|
|
}
|
|
|
|
let kniha = Kniha {
|
|
nazov: "Duna".into(),
|
|
stav: Stav::Nova,
|
|
};
|
|
|
|
let json = serde_json::to_string_pretty(&kniha).unwrap();
|
|
// {
|
|
// "nazov": "Duna",
|
|
// "stav": "Nova"
|
|
// }
|
|
```
|
|
|
|
### Úlohy 4
|
|
|
|
**4a.** Vytvor enum `Priorita` s hodnotami `Nizka, Stredna, Vysoka`. Serializuj každú hodnotu. Aký JSON produkujú?
|
|
|
|
**4b.** Vytvor štruktúru `Uloha { nazov: String, priorita: Priorita }`. Serializuj jednu úlohu. Deserializuj JSON späť.
|
|
|
|
**4c.** Vytvor enum s dátami:
|
|
```rust
|
|
enum Odpoved {
|
|
Spravna,
|
|
Nespravna { pokus: u32 },
|
|
Nevyplnena,
|
|
}
|
|
```
|
|
Serializuj každú variantu a pozri aký JSON generujú.
|
|
|
|
---
|
|
|
|
## Kapitola 5: Súborový I/O — načítanie a uloženie
|
|
|
|
Toto je na **každej** skúške. Vždy rovnaký vzor.
|
|
|
|
### Uloženie do súboru
|
|
|
|
```rust
|
|
use std::fs;
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
struct Kniznica {
|
|
knihy: Vec<Kniha>,
|
|
}
|
|
|
|
impl Kniznica {
|
|
fn uloz_do_suboru(&self, cesta: &std::path::PathBuf) -> bool {
|
|
// 1. Serializuj na JSON string
|
|
let Ok(json) = serde_json::to_string_pretty(&self) else {
|
|
return false;
|
|
};
|
|
// 2. Zapíš do súboru
|
|
fs::write(cesta, json).is_ok()
|
|
}
|
|
}
|
|
```
|
|
|
|
### Načítanie zo súboru
|
|
|
|
```rust
|
|
impl Kniznica {
|
|
fn nacitaj_zo_suboru(cesta: &std::path::PathBuf) -> Option<Kniznica> {
|
|
// 1. Prečítaj súbor
|
|
let raw = fs::read_to_string(cesta).ok()?;
|
|
// 2. Deserializuj JSON string na štruktúru
|
|
serde_json::from_str(&raw).ok()
|
|
}
|
|
}
|
|
```
|
|
|
|
**Prečo `.ok()?`:**
|
|
- `fs::read_to_string` vracia `Result<String, io::Error>`
|
|
- `.ok()` ho premení na `Option<String>` (zahodí chybu)
|
|
- `?` vráti `None` ak bol výsledok `None`, inak rozbalí hodnotu
|
|
|
|
**Prečo posledný riadok nemá `?`:**
|
|
- `serde_json::from_str` vracia `Result<T, Error>`
|
|
- `.ok()` ho premení na `Option<T>`
|
|
- Toto je posledný výraz funkcie, takže sa priamo vráti
|
|
|
|
### Kompletný vzor (toto si zapamätaj naspamäť)
|
|
|
|
```rust
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
struct MojaStruktura {
|
|
data: Vec<String>,
|
|
}
|
|
|
|
impl MojaStruktura {
|
|
fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<MojaStruktura> {
|
|
let raw = fs::read_to_string(cesta).ok()?;
|
|
serde_json::from_str(&raw).ok()
|
|
}
|
|
|
|
fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
|
|
let Ok(json) = serde_json::to_string_pretty(&self) else {
|
|
return false;
|
|
};
|
|
fs::write(cesta, json).is_ok()
|
|
}
|
|
}
|
|
```
|
|
|
|
Toto sa na skúške nikdy nemení. Len názov štruktúry sa zmení.
|
|
|
|
### Úlohy 5
|
|
|
|
**5a.** Napíš `nacitaj_zo_suboru` a `uloz_do_suboru` pre:
|
|
```rust
|
|
#[derive(Serialize, Deserialize, Default)]
|
|
struct Adresar {
|
|
kontakty: Vec<Kontakt>,
|
|
}
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
struct Kontakt {
|
|
meno: String,
|
|
telefon: String,
|
|
}
|
|
```
|
|
|
|
**5b.** Napíš program, ktorý:
|
|
1. Vytvorí `Adresar` s 2 kontaktmi
|
|
2. Uloží ho do `"adresar.json"`
|
|
3. Načíta ho späť
|
|
4. Vypíše počet kontaktov
|
|
|
|
**5c.** Čo sa stane keď:
|
|
- Súbor neexistuje a voláš `nacitaj_zo_suboru`?
|
|
- Súbor obsahuje neplatný JSON?
|
|
- Súbor obsahuje platný JSON ale inej štruktúry?
|
|
|
|
Vyskúšaj všetky 3 prípady.
|
|
|
|
---
|
|
|
|
## Kapitola 6: Derive traity — čo kedy pridať
|
|
|
|
Na skúške musíš vedieť aké derive traity pridať. Tu je prehľad:
|
|
|
|
### Serialize a Deserialize
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize)]
|
|
```
|
|
|
|
**Kedy:** Vždy keď štruktúra/enum sa má ukladať do JSON alebo načítať z JSON.
|
|
|
|
**Požiadavka:** Všetky polia musia tiež implementovať Serialize/Deserialize. Štandardné typy (String, i32, Vec, HashMap, Option...) to majú.
|
|
|
|
### Default
|
|
|
|
```rust
|
|
#[derive(Default)]
|
|
```
|
|
|
|
**Kedy:** Keď chceš vytvoriť inštanciu s defaultnými hodnotami.
|
|
|
|
```rust
|
|
#[derive(Default, Serialize, Deserialize)]
|
|
struct Kniznica {
|
|
knihy: Vec<Kniha>, // default = prázdny Vec
|
|
}
|
|
|
|
// Použitie:
|
|
let kniznica = Kniznica::default();
|
|
// knihy = []
|
|
|
|
// Na skúške typicky:
|
|
let kniznica = Kniznica::nacitaj_zo_suboru(&cesta)
|
|
.unwrap_or_default(); // ak súbor neexistuje, prázdna knižnica
|
|
```
|
|
|
|
Defaultné hodnoty:
|
|
- `String` → `""`
|
|
- `Vec<T>` → `vec![]`
|
|
- `i32, u32, f64...` → `0`
|
|
- `bool` → `false`
|
|
- `Option<T>` → `None`
|
|
|
|
### Clone
|
|
|
|
```rust
|
|
#[derive(Clone)]
|
|
```
|
|
|
|
**Kedy:** Keď potrebuješ kópiu štruktúry. Toto potrebuješ ak:
|
|
- Vraciaš štruktúru z vektora (nie referenciu)
|
|
- Ukladáš kópiu niekam
|
|
- Voláš `.clone()` na štruktúre
|
|
|
|
### Debug
|
|
|
|
```rust
|
|
#[derive(Debug)]
|
|
```
|
|
|
|
**Kedy:** Keď chceš `println!("{:?}", struktura)`. Užitočné pri debugovaní.
|
|
|
|
### PartialEq
|
|
|
|
```rust
|
|
#[derive(PartialEq)]
|
|
```
|
|
|
|
**Kedy:** Keď chceš porovnávať `==` a `!=` medzi inštanciami.
|
|
|
|
```rust
|
|
#[derive(PartialEq)]
|
|
enum Stav { Nova, Pouzivana }
|
|
|
|
let a = Stav::Nova;
|
|
let b = Stav::Nova;
|
|
println!("{}", a == b); // true — funguje vďaka PartialEq
|
|
```
|
|
|
|
### Kombinované — typický vzor zo skúšky
|
|
|
|
```rust
|
|
// Enum — zvyčajne všetko
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
enum Stav {
|
|
Nova,
|
|
Pouzivana,
|
|
Poskodena,
|
|
Vyradena,
|
|
}
|
|
|
|
// Vnorená štruktúra — Serialize, Deserialize, Clone
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
struct Kniha {
|
|
nazov: String,
|
|
stav: Stav,
|
|
}
|
|
|
|
// Hlavná štruktúra — Serialize, Deserialize, Default
|
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
|
struct Kniznica {
|
|
knihy: Vec<Kniha>,
|
|
}
|
|
```
|
|
|
|
### Úlohy 6
|
|
|
|
**6a.** Zadanie hovorí:
|
|
```
|
|
Enum Stav: Nova, Pouzivana, Poskodena, Vyradena
|
|
Požadované traity: Default, Display, Serialize, Deserialize, PartialEq
|
|
Default vráti hodnotu Nova
|
|
```
|
|
Implementuj to. Pozor: `Default` a `Display` sa nedajú derive automaticky pre enum s vlastnou default hodnotou — musíš ich implementovať ručne.
|
|
|
|
**6b.** Prečo knižnica (hlavná štruktúra) potrebuje `Default` ale kniha (vnorená štruktúra) nie?
|
|
|
|
**6c.** Kedy potrebuješ `Clone` na štruktúre? Uveď 2 príklady kódu, kde by bez Clone kompilácia zlyhala.
|
|
|
|
---
|
|
|
|
## Kapitola 7: Option v serde
|
|
|
|
`Option<T>` sa serializuje špeciálne:
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
struct Profil {
|
|
meno: String,
|
|
prezyvka: Option<String>,
|
|
vek: Option<u32>,
|
|
}
|
|
|
|
// Some → hodnota
|
|
let profil = Profil {
|
|
meno: "Anna".into(),
|
|
prezyvka: Some("Anka".into()),
|
|
vek: Some(25),
|
|
};
|
|
// {"meno":"Anna","prezyvka":"Anka","vek":25}
|
|
|
|
// None → null
|
|
let profil = Profil {
|
|
meno: "Boris".into(),
|
|
prezyvka: None,
|
|
vek: None,
|
|
};
|
|
// {"meno":"Boris","prezyvka":null,"vek":null}
|
|
```
|
|
|
|
**Deserializácia:**
|
|
```rust
|
|
// Pole chýba v JSON → None (ak je Option)
|
|
let json = r#"{"meno":"Cyril"}"#;
|
|
// BEZ špeciálnej konfigurácie: CHYBA! serde čaká všetky polia.
|
|
|
|
// S atribútom skip_serializing_if + default:
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
struct Profil {
|
|
meno: String,
|
|
#[serde(default)] // ak chýba v JSON, použi Default (None pre Option)
|
|
prezyvka: Option<String>,
|
|
#[serde(default)]
|
|
vek: Option<u32>,
|
|
}
|
|
// Teraz: {"meno":"Cyril"} → Profil { meno: "Cyril", prezyvka: None, vek: None }
|
|
```
|
|
|
|
Na skúške väčšinou nemusíš riešiť `#[serde(default)]` — JSON vždy obsahuje všetky polia. Ale je dobré vedieť, že to existuje.
|
|
|
|
### Úlohy 7
|
|
|
|
**7a.** Vytvor štruktúru `Student { meno: String, email: Option<String>, vek: u32 }`. Serializuj študenta s emailom aj bez. Porovnaj JSON.
|
|
|
|
**7b.** Deserializuj tento JSON na štruktúru:
|
|
```json
|
|
{"meno":"Anna","email":null,"vek":25}
|
|
```
|
|
Čo bude hodnota poľa `email`?
|
|
|
|
**7c.** Máš Vec<Student>. Vyfiltruj len tých, ktorí majú email (email.is_some()). Serializuj výsledok.
|
|
|
|
---
|
|
|
|
## Kapitola 8: HashMap a Vec v serde
|
|
|
|
### Vec → JSON pole
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Trieda {
|
|
nazov: String,
|
|
studenti: Vec<String>,
|
|
}
|
|
|
|
let trieda = Trieda {
|
|
nazov: "4.A".into(),
|
|
studenti: vec!["Anna".into(), "Boris".into()],
|
|
};
|
|
// {
|
|
// "nazov": "4.A",
|
|
// "studenti": ["Anna", "Boris"]
|
|
// }
|
|
```
|
|
|
|
### HashMap → JSON objekt
|
|
|
|
```rust
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Slovnik {
|
|
preklady: HashMap<String, String>,
|
|
}
|
|
|
|
let mut preklady = HashMap::new();
|
|
preklady.insert("dog".into(), "pes".into());
|
|
preklady.insert("cat".into(), "mačka".into());
|
|
|
|
let slovnik = Slovnik { preklady };
|
|
// {
|
|
// "preklady": {
|
|
// "dog": "pes",
|
|
// "cat": "mačka"
|
|
// }
|
|
// }
|
|
```
|
|
|
|
**Dôležité:** HashMap kľúče musia byť String (alebo čísla) v JSON. JSON nepodporuje iné typy kľúčov.
|
|
|
|
### Vnorené štruktúry
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Adresa {
|
|
ulica: String,
|
|
mesto: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Osoba {
|
|
meno: String,
|
|
adresa: Adresa, // vnorený objekt
|
|
telefony: Vec<String>, // pole
|
|
}
|
|
|
|
// {
|
|
// "meno": "Anna",
|
|
// "adresa": {
|
|
// "ulica": "Hlavná 1",
|
|
// "mesto": "Bratislava"
|
|
// },
|
|
// "telefony": ["+421 900 000 000"]
|
|
// }
|
|
```
|
|
|
|
### Úlohy 8
|
|
|
|
**8a.** Vytvor štruktúru `SpravcaHry` s poľom `slovnik: HashMap<String, Vec<String>>` (kategória → slová). Serializuj s 2 kategóriami, každá s 3 slovami. Pozri JSON.
|
|
|
|
**8b.** Deserializuj tento JSON:
|
|
```json
|
|
{
|
|
"meno": "Knižnica",
|
|
"knihy": [
|
|
{"nazov": "Duna", "rok": 1965},
|
|
{"nazov": "Hobbit", "rok": 1937}
|
|
]
|
|
}
|
|
```
|
|
Aké štruktúry potrebuješ?
|
|
|
|
**8c.** Máš `HashMap<String, Vec<String>>`. Serializuj ho priamo (nie v štruktúre). Čo je výsledok?
|
|
|
|
---
|
|
|
|
## Kapitola 9: serde_json::Value — dynamický JSON
|
|
|
|
Niekedy nevieš aký tvar bude mať JSON. Alebo ti stačí prísť len k jednej hodnote.
|
|
|
|
```rust
|
|
use serde_json::Value;
|
|
|
|
let json = r#"{"meno":"Anna","vek":25,"aktívna":true}"#;
|
|
|
|
// Parsuj do Value — generického JSON typu
|
|
let v: Value = serde_json::from_str(json).unwrap();
|
|
|
|
// Prístup k hodnotám cez ["kľúč"]
|
|
println!("{}", v["meno"]); // "Anna"
|
|
println!("{}", v["vek"]); // 25
|
|
println!("{}", v["aktívna"]); // true
|
|
println!("{}", v["neexistuje"]); // null
|
|
|
|
// Konverzia na Rust typy
|
|
let meno: Option<&str> = v["meno"].as_str(); // Some("Anna")
|
|
let vek: Option<u64> = v["vek"].as_u64(); // Some(25)
|
|
let aktivna: Option<bool> = v["aktívna"].as_bool(); // Some(true)
|
|
```
|
|
|
|
**Typy v Value:**
|
|
```rust
|
|
enum Value {
|
|
Null,
|
|
Bool(bool),
|
|
Number(Number),
|
|
String(String),
|
|
Array(Vec<Value>),
|
|
Object(Map<String, Value>),
|
|
}
|
|
```
|
|
|
|
**Vytváranie JSON dynamicky:**
|
|
```rust
|
|
use serde_json::json;
|
|
|
|
// json! makro — vytvor Value rýchlo
|
|
let v = json!({
|
|
"meno": "Anna",
|
|
"vek": 25,
|
|
"zaujmy": ["šach", "čítanie"],
|
|
"adresa": {
|
|
"mesto": "Bratislava"
|
|
}
|
|
});
|
|
|
|
println!("{}", v["zaujmy"][0]); // "šach"
|
|
```
|
|
|
|
### Úlohy 9
|
|
|
|
**9a.** Máš neznámy JSON. Parsuj ho do `Value`. Skontroluj či obsahuje kľúč "meno". Ak áno, vypíš hodnotu.
|
|
|
|
**9b.** Vytvor JSON pomocou `json!` makra s vnoreným objektom a poľom. Serializuj ho do pretty stringu.
|
|
|
|
**9c.** Kedy použiješ `Value` a kedy vlastnú štruktúru?
|
|
|
|
---
|
|
|
|
## Kapitola 10: Atribúty — #[serde(...)]
|
|
|
|
serde má atribúty, ktorými ovládaš ako sa serializuje/deserializuje.
|
|
|
|
### rename — premenuj pole
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Osoba {
|
|
#[serde(rename = "full_name")]
|
|
meno: String,
|
|
vek: u32,
|
|
}
|
|
// {"full_name":"Anna","vek":25}
|
|
```
|
|
|
|
### rename_all — premenuj všetky polia
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct Osoba {
|
|
cele_meno: String,
|
|
datum_narodenia: String,
|
|
}
|
|
// {"celeMeno":"Anna","datumNarodenia":"2000-01-01"}
|
|
|
|
// Ďalšie možnosti:
|
|
// "snake_case" → cele_meno
|
|
// "camelCase" → celeMeno
|
|
// "PascalCase" → CeleMeno
|
|
// "SCREAMING_SNAKE_CASE" → CELE_MENO
|
|
// "kebab-case" → cele-meno
|
|
```
|
|
|
|
### skip — vynechaj pole
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Osoba {
|
|
meno: String,
|
|
#[serde(skip)] // toto pole sa neserializuje ani nedeserializuje
|
|
docasne_data: String,
|
|
}
|
|
```
|
|
|
|
### default — default hodnota ak chýba v JSON
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Config {
|
|
#[serde(default)]
|
|
port: u16, // ak chýba → 0
|
|
#[serde(default = "default_host")]
|
|
host: String, // ak chýba → "localhost"
|
|
}
|
|
|
|
fn default_host() -> String {
|
|
"localhost".to_string()
|
|
}
|
|
```
|
|
|
|
### deny_unknown_fields — zakáž extra polia
|
|
|
|
```rust
|
|
#[derive(Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct Osoba {
|
|
meno: String,
|
|
vek: u32,
|
|
}
|
|
// {"meno":"Anna","vek":25,"extra":"niečo"} → CHYBA!
|
|
```
|
|
|
|
### Úlohy 10
|
|
|
|
**10a.** Máš API, ktoré vracia JSON s camelCase kľúčmi:
|
|
```json
|
|
{"firstName":"Anna","lastName":"Nováková","dateOfBirth":"2000-01-01"}
|
|
```
|
|
Napíš Rust štruktúru, ktorá toto deserializuje. Použi `#[serde(rename_all = "camelCase")]`.
|
|
|
|
**10b.** Máš štruktúru s poľom `heslo: String`, ktoré sa nesmie objaviť v JSON. Použi `#[serde(skip)]`.
|
|
|
|
**10c.** Máš config štruktúru. Niektoré polia sú voliteľné a majú default hodnotu. Použi `#[serde(default)]`.
|
|
|
|
---
|
|
|
|
## Kapitola 11: Chybové stavy a bezpečné spracovanie
|
|
|
|
### Result z serde_json
|
|
|
|
Obe hlavné funkcie vracajú Result:
|
|
|
|
```rust
|
|
// Serializácia — skoro nikdy nezlyhá (len ak typ nie je serializovateľný)
|
|
let vysledok: Result<String, serde_json::Error> = serde_json::to_string(&data);
|
|
|
|
// Deserializácia — zlyhá často (neplatný JSON, zlá štruktúra)
|
|
let vysledok: Result<MojTyp, serde_json::Error> = serde_json::from_str(json);
|
|
```
|
|
|
|
### Vzory spracovania chýb
|
|
|
|
```rust
|
|
// 1. unwrap — program spadne ak chyba (len pre testovacie účely!)
|
|
let data: MojTyp = serde_json::from_str(json).unwrap();
|
|
|
|
// 2. ? — propaguj chybu nahor
|
|
fn spracuj(json: &str) -> Result<MojTyp, serde_json::Error> {
|
|
let data: MojTyp = serde_json::from_str(json)?;
|
|
Ok(data)
|
|
}
|
|
|
|
// 3. .ok() — zahoď chybu, vráť Option
|
|
let data: Option<MojTyp> = serde_json::from_str(json).ok();
|
|
|
|
// 4. .ok()? — v funkcii vracajúcej Option (TOTO JE VZOR ZO SKÚŠKY)
|
|
fn nacitaj(cesta: &PathBuf) -> Option<MojTyp> {
|
|
let raw = fs::read_to_string(cesta).ok()?;
|
|
serde_json::from_str(&raw).ok()
|
|
}
|
|
|
|
// 5. match — keď chceš reagovať na chybu
|
|
match serde_json::from_str::<MojTyp>(json) {
|
|
Ok(data) => println!("OK: {:?}", data),
|
|
Err(e) => println!("Chyba: {}", e),
|
|
}
|
|
|
|
// 6. let..else — keď chceš hodnotu alebo early return
|
|
let Ok(json) = serde_json::to_string_pretty(&self) else {
|
|
return false;
|
|
};
|
|
```
|
|
|
|
### Úlohy 11
|
|
|
|
**11a.** Napíš funkciu `bezpecne_parsuj(json: &str) -> Option<Kniha>` — vráti None pri akejkoľvek chybe.
|
|
|
|
**11b.** Napíš funkciu `parsuj_alebo_default(json: &str) -> Kniznica` — ak sa nepodarí parsovať, vráti prázdnu knižnicu. Použi `.unwrap_or_default()`.
|
|
|
|
**11c.** Napíš funkciu `nacitaj_a_pridaj(cesta: &PathBuf, kniha: Kniha) -> bool`:
|
|
1. Načítaj knižnicu zo súboru (ak neexistuje, vytvor prázdnu)
|
|
2. Pridaj knihu
|
|
3. Ulož späť
|
|
4. Vráť true ak sa podarilo
|
|
|
|
---
|
|
|
|
## Kapitola 12: Praktické vzory zo skúšky
|
|
|
|
### Vzor 1: Kompletný skúškový vzor (lib.rs)
|
|
|
|
```rust
|
|
use serde::{Serialize, Deserialize};
|
|
use std::collections::HashMap;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub enum Stav {
|
|
Nova,
|
|
Pouzivana,
|
|
Poskodena,
|
|
Vyradena,
|
|
}
|
|
|
|
impl Default for Stav {
|
|
fn default() -> Self {
|
|
Stav::Nova
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Kniha {
|
|
pub nazov: String,
|
|
pub autor: String,
|
|
pub rok: u16,
|
|
pub stav: Stav,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
|
pub struct Kniznica {
|
|
pub knihy: Vec<Kniha>,
|
|
}
|
|
|
|
impl Kniznica {
|
|
pub fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<Kniznica> {
|
|
let raw = fs::read_to_string(cesta).ok()?;
|
|
serde_json::from_str(&raw).ok()
|
|
}
|
|
|
|
pub fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
|
|
let Ok(json) = serde_json::to_string_pretty(&self) else {
|
|
return false;
|
|
};
|
|
fs::write(cesta, json).is_ok()
|
|
}
|
|
|
|
pub fn pridaj_knihu(&mut self, kniha: Kniha) -> Result<(), ()> {
|
|
if self.knihy.iter().any(|k| k.nazov == kniha.nazov) {
|
|
return Err(());
|
|
}
|
|
self.knihy.push(kniha);
|
|
Ok(())
|
|
}
|
|
|
|
// ... ďalšie metódy
|
|
}
|
|
```
|
|
|
|
### Vzor 2: main.rs s načítaním
|
|
|
|
```rust
|
|
use std::path::PathBuf;
|
|
|
|
fn main() {
|
|
let cesta = PathBuf::from("kniznica.json");
|
|
|
|
// Načítaj alebo vytvor novú
|
|
let mut kniznica = Kniznica::nacitaj_zo_suboru(&cesta)
|
|
.unwrap_or_default();
|
|
|
|
// Urob niečo
|
|
let kniha = Kniha {
|
|
nazov: "Duna".into(),
|
|
autor: "Herbert".into(),
|
|
rok: 1965,
|
|
stav: Stav::Nova,
|
|
};
|
|
let _ = kniznica.pridaj_knihu(kniha);
|
|
|
|
// Ulož
|
|
kniznica.uloz_do_suboru(&cesta);
|
|
}
|
|
```
|
|
|
|
### Vzor 3: SpravcaHry s HashMap (Obesenec)
|
|
|
|
```rust
|
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
|
pub struct SpravcaHry {
|
|
pub slovnik: HashMap<String, Vec<String>>,
|
|
}
|
|
|
|
impl SpravcaHry {
|
|
pub fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<SpravcaHry> {
|
|
let raw = fs::read_to_string(cesta).ok()?;
|
|
serde_json::from_str(&raw).ok()
|
|
}
|
|
|
|
pub fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
|
|
let Ok(json) = serde_json::to_string_pretty(&self) else {
|
|
return false;
|
|
};
|
|
fs::write(cesta, json).is_ok()
|
|
}
|
|
|
|
pub fn pridaj_kategoriu(&mut self, kategoria: &str) -> Result<(), ()> {
|
|
if self.slovnik.contains_key(kategoria) {
|
|
return Err(());
|
|
}
|
|
self.slovnik.insert(kategoria.to_string(), Vec::new());
|
|
Ok(())
|
|
}
|
|
|
|
pub fn pridaj_slovo(&mut self, kategoria: &str, slovo: &str) -> Result<(), ()> {
|
|
let slova = self.slovnik.get_mut(kategoria).ok_or(())?;
|
|
slova.push(slovo.to_string());
|
|
Ok(())
|
|
}
|
|
}
|
|
```
|
|
|
|
JSON pre SpravcaHry:
|
|
```json
|
|
{
|
|
"slovnik": {
|
|
"zvierata": ["pes", "mačka", "kôň"],
|
|
"jedlo": ["pizza", "burger", "šalát"]
|
|
}
|
|
}
|
|
```
|
|
|
|
### Úlohy 12
|
|
|
|
**12a.** Implementuj kompletný vzor pre **Milionár** zo skúšky:
|
|
```
|
|
Štruktúra Odpoved: text (String), je_spravna (bool)
|
|
Štruktúra Otazka: text (String), odpovede (Vec<Odpoved>)
|
|
Štruktúra Milionar: otazky (Vec<Otazka>)
|
|
|
|
+ nacitaj_zo_suboru, uloz_do_suboru
|
|
```
|
|
Pridaj správne derive traity. Vytvor testovacie dáta, ulož do JSON, načítaj späť.
|
|
|
|
**12b.** Vytvor JSON ručne (v .json súbore alebo v kóde cez `r#"..."#`). Potom ho načítaj do tvojej štruktúry. Over, že všetky hodnoty sedia.
|
|
|
|
**12c.** Napíš funkciu, ktorá načíta ľubovoľný JSON ako `Value`, overí či obsahuje kľúč `"verzia"`, a ak áno, vypíše jeho hodnotu.
|