Files
JR-priprava-na-skusku/priprava/rust_priprava9.md
2026-03-04 15:55:29 +01:00

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.