---
title: "Parse YAML into Rust structs"
url: https://recursiveai.org/oneyaml/rust/cookbook/parse-yaml-into-structs/
description: "Read a YAML file into Rust structs and enums with serde and oneyaml, with renamed fields, defaults, optional sections and an enum."
version: "1.2.0"
---

# Parse YAML into Rust structs

Read a YAML file into typed Rust data with serde: nested structs, a map, an optional section, a default and an enum. oneyaml is a serde data format like `serde_json`, so the same `#[serde(...)]` attributes apply.

```sh
cargo add oneyaml@1.2.0 serde -F serde/derive
```

The file:

```yaml
# service.yaml
name: web
image: nginx:1.27
env:
  LOG_LEVEL: info
  PORT: "8080"
healthcheck:
  path: /healthz
  intervalSeconds: 10
restart: on-failure
```

The program:

```rust
use serde::Deserialize;
use std::collections::BTreeMap;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Service {
    name: String,
    image: String,
    #[serde(default = "one")]
    replica_count: u32,
    env: BTreeMap<String, String>,
    healthcheck: Option<Healthcheck>,
    restart: Restart,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Healthcheck {
    path: String,
    interval_seconds: u32,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
enum Restart {
    Always,
    OnFailure,
    Never,
}

fn one() -> u32 {
    1
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let src = std::fs::read_to_string("service.yaml")?;
    let svc: Service = oneyaml::from_str(&src)?;

    println!("{} runs {} x{}", svc.name, svc.image, svc.replica_count);
    println!("env: {:?}", svc.env);
    if let Some(h) = &svc.healthcheck {
        println!("health check: {} every {}s", h.path, h.interval_seconds);
    }
    match svc.restart {
        Restart::Always => println!("restart: always"),
        Restart::OnFailure => println!("restart: on failure"),
        Restart::Never => println!("restart: never"),
    }
    Ok(())
}
```

```text
web runs nginx:1.27 x1
env: {"LOG_LEVEL": "info", "PORT": "8080"}
health check: /healthz every 10s
restart: on failure
```

## Good to know

- `replicaCount` isn’t in the file, so `#[serde(default = "one")]` fills it. Without a default, a missing field is an error that names it.
- `PORT: "8080"` is quoted, so it reads as a string into the `BTreeMap<String, String>`. Unquoted, `8080` is an integer, and reading it into a `String` is a type error. oneyaml doesn’t convert scalars behind your back.
- The YAML 1.2 Core schema is the default: plain `yes`, `no`, `on` and `off` are strings, and `0755` is 755. To read a file written for YAML 1.1, see [YAML 1.1 booleans](https://recursiveai.org/oneyaml/rust/cookbook/yaml-1-1-booleans/).
- `from_slice` and `from_reader` read bytes and any `io::Read`. Each has a `_with` form that takes `Settings`.

When the shape isn’t known ahead of time, read a `Value` instead: [read YAML without a schema](https://recursiveai.org/oneyaml/rust/cookbook/read-yaml-without-a-schema/). A field of the wrong type is reported with its line, column and path: [errors with line and column](https://recursiveai.org/oneyaml/rust/cookbook/errors-with-line-and-column/).
