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

24 KiB

serde a serde_json — krok za krokom

Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.

Cargo.toml:

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
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()]:

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

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
  • booltrue/false
  • Vec<T>[...]
  • Option<T> → hodnota alebo null
  • HashMap<String, T>{...} objekt
  • vnorené štruktúry → vnorené objekty
#[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

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í?

// 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:

{"nazov":"Duna","autor":"Frank Herbert","rok":1965}

Deserializuj ho do štruktúry Kniha. Vypíš názov a rok.

3b. Máš JSON pole:

[{"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

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
// 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:

#[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:

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

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

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äť)

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:

#[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

#[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

#[derive(Default)]

Kedy: Keď chceš vytvoriť inštanciu s defaultnými hodnotami.

#[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
  • boolfalse
  • Option<T>None

Clone

#[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

#[derive(Debug)]

Kedy: Keď chceš println!("{:?}", struktura). Užitočné pri debugovaní.

PartialEq

#[derive(PartialEq)]

Kedy: Keď chceš porovnávať == a != medzi inštanciami.

#[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

// 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:

#[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:

// 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:

{"meno":"Anna","email":null,"vek":25}

Čo bude hodnota poľa email?

7c. Máš Vec. Vyfiltruj len tých, ktorí majú email (email.is_some()). Serializuj výsledok.


Kapitola 8: HashMap a Vec v serde

Vec → JSON pole

#[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

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

#[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:

{
  "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.

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:

enum Value {
    Null,
    Bool(bool),
    Number(Number),
    String(String),
    Array(Vec<Value>),
    Object(Map<String, Value>),
}

Vytváranie JSON dynamicky:

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

#[derive(Serialize, Deserialize)]
struct Osoba {
    #[serde(rename = "full_name")]
    meno: String,
    vek: u32,
}
// {"full_name":"Anna","vek":25}

rename_all — premenuj všetky polia

#[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

#[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

#[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

#[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:

{"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:

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

// 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)

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

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)

#[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:

{
  "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.