CodeBlanks Logo Icon CodeBlanks

Reading JSON

Read a list of posts with serde_json.

Reading the data

In a real project posts are read from a JSON array in a single line:

let metin = fs::read_to_string("yazilar.json")?;
let yazilar: Vec<Yazi> = serde_json::from_str(&metin)?;

Since there are no external crates in the exercise, we'll use a simpler record format: one record per line, fields separated by |, tags by commas:

1|Rust ile başlangıç|ece|rust,giriş|true
2|Sahiplik|can|rust|false

Parsing a record

The record is split at |; if the number of fields is wrong or a field can't be converted, None is returned. A slice pattern names the five fields in one step:

fn kayittan(s: &str) -> Option<Yazi> {
    let alanlar: Vec<&str> = s.split('|').collect();
    let [id, konu, yazar, etiket, yayinda] = alanlar[..] else {
        return None;
    };
    Some(Yazi {
        id: id.parse().ok()?,
        konu: konu.to_string(),
        yazar: yazar.to_string(),
        etiketler: etiket.split(',').map(String::from).collect(),
        yayinda: yayinda.parse().ok()?,
    })
}

Practice Read the records

Write the kayittan function above. Read three lines of data (two valid, the third 3|bozuk kayıt) with lines() and filter_map; print it as 2 kayıt: topic1, topic2.

Starter code

main.rs
#[derive(Debug, Clone)]
struct Yazi {
    id: u32,
    konu: String,
    yazar: String,
    etiketler: Vec<String>,
    yayinda: bool,
}

fn main() {
    // buraya yaz
}

Sample solution

main.rs
#[derive(Debug, Clone)]
struct Yazi {
    id: u32,
    konu: String,
    yazar: String,
    etiketler: Vec<String>,
    yayinda: bool,
}

fn kayittan(s: &str) -> Option<Yazi> {
    let alanlar: Vec<&str> = s.split('|').collect();
    let [id, konu, yazar, etiket, yayinda] = alanlar[..] else {
        return None;
    };
    Some(Yazi {
        id: id.parse().ok()?,
        konu: konu.to_string(),
        yazar: yazar.to_string(),
        etiketler: etiket.split(',').map(String::from).collect(),
        yayinda: yayinda.parse().ok()?,
    })
}

fn main() {
    let veri = "1|Rust ile başlangıç|ece|rust,giriş|true\n2|Sahiplik|can|rust|false\n3|bozuk kayıt";
    let yazilar: Vec<Yazi> = veri.lines().filter_map(kayittan).collect();
    let konular: Vec<&str> = yazilar.iter().map(|y| y.konu.as_str()).collect();
    println!("{} kayıt: {}", yazilar.len(), konular.join(", "));
}

Expected output

2 kayıt: Rust ile başlangıç, Sahiplik

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What is the risk of silently skipping a broken record?

  • The program slows down
  • Compile error
  • There's no risk
  • Data loss goes unnoticed; at least the count should be reported Correct answer

Explanation: In this lesson we skip broken ones; in the next lesson we'll report each error with its number.

2 Collect the valid ones

Question
let yazilar: Vec<Yazi> = veri.lines().____(kayittan).collect();
Answer
let yazilar: Vec<Yazi> = veri.lines().filter_map(kayittan).collect();

Explanation: filter_map drops the None results and collects what's inside Some.

3 Record and result

Term Meaning
1|Rust|ece|rust|true Some(Yazi)
3|bozuk kayıt None (fields missing)
x|a|b|c|true None (invalid id)
1|a|b|c|evet None (invalid bool)

All lessons in Intermediate