Serde_yaml: "invalid type: map, expected unit"

Hello,

I am using serde_yaml to deserialize a specific file based off of YAML. The following line of code:

let values = serde_yaml::from_reader(f)?;

Gives the error in the title. I put it in an online YAML validator, and it was valid. I assume the problem is user error, but I'm not sure how to fix it...

Any advice is appreciated :slight_smile:

Unsure if this would be the problem, but some of your YAML maps have improperly indented lists, which would put the list items in the global scope instead of under the item name. This would conflict with serde_yaml's interpretation of the file as map.

Your YAML (line 184):

exp_selectors:
- dyn
- pitd
- clr
- eng
- vel

What it probably should be:

exp_selectors:
  - dyn
  - pitd
  - clr
  - eng
  - vel

No luck after fixing that :frowning:

What structure are you trying to parse it into? Like, how do your structs with #[derive(Derserialize) look like? It sounds like they don't match your structure inside the file.

1 Like

Based on what I see in YAML specification no, this list is actually properly indented. I am referring specifically to this paragraph:

The โ€œ- โ€, โ€œ? โ€ and โ€œ: โ€ characters used to denote block collection entries are perceived by people to be part of the indentation. This is handled on a case-by-case basis by the relevant productions.

This is the struct:

#[derive(Deserialize, Debug)]
struct UstxFile {
    name: String,
    comment: String,
    output_dir: String,
    cache_dir: String,
    ustx_version: f32,
    resolution: isize,
    bpm: isize,
    beat_per_bar: isize,
    beat_unit: isize,
    expressions: Vec<Expression>,
    exp_selectors: Vec<String>,
    exp_primary: isize,
    exp_secondary: isize,
    key: isize,
    time_signatures: Vec<TimeSig>,
    tempos: Vec<Tempo>,
    tracks: Vec<Track>
}

#[derive(Deserialize, Debug)]
struct Part {
    name: String,
    comment: String,
    track_no: isize,
    position: isize,
}

#[derive(Deserialize, Debug)]
struct Note {
    position: isize,
    duration: isize,
    tone: isize,
    lyric: String,
    pitch: PitchData,
    vibrato: Vibrato,
    phoneme_expressions: Vec<PhonemeExpression>
}

#[derive(Deserialize, Debug)]
struct PhonemeOverride {
    index: usize,
    overlap_delta: f64
}

#[derive(Deserialize, Debug)]
struct PhonemeExpression {
    index: usize,
    abbr: String,
    value: isize
}

#[derive(Deserialize, Debug)]
struct Vibrato {
    length: isize, 
    period: isize, 
    depth: isize, 
    fade_in: isize, 
    fade_out: isize,
    shift: isize, 
    drift: isize, 
    vol_link: isize
}

#[derive(Deserialize, Debug)]
struct Pitch {
    data: Vec<PitchData>,
    snap_first: bool
}

#[derive(Deserialize, Debug)]
struct PitchData {
    x: f64,
    y: f64,
    shape: String
}

#[derive(Deserialize, Debug)]
struct Track {
    singer: String,
    phonemizer: String,
    renderer_settings: RendererSettings,
    track_name: String,
    track_color: String,
    mute: bool,
    solo: bool,
    volume: f64,
    pan: f64,
    voice_color_names: Vec<String>
}

#[derive(Deserialize, Debug)]
struct RendererSettings {

}

#[derive(Deserialize, Debug)]
struct Expression {
    name: String,
    abbr: String,
    value_type: String, // change this to enum
    min: isize,
    max: isize,
    default_value: isize,
    is_flag: bool,
    flag: String
}

#[derive(Deserialize, Debug)]
struct TimeSig {
    bar_position: isize,
    beat_per_bar: isize,
    beat_unit: isize,
}

#[derive(Deserialize, Debug)]
struct Tempo {
    position: isize,
    bpm: isize
}

I realized afterwards that if I explicitly declare the type as UstxFile (which I forgot to do... ugh), it gives... A different error? It says "missing field comment." I tried deleting both the comment variable from the struct and the comment from the file, and it just said it was missing the next line (output_dir) then. So, it seems that, for whatever reason, only the first line is being acknowledged?

I'm not familiar with from_reader, but using from_str has an error only in the 6th line (ustx_version): invalid type: string "0.6", expected f32.

I'm not sure why, but on my system, using from_str() (and changing ustx_version to String) gives me Error: Error("missing field beat_unit", line: 1, column: 1) instead.

My main function:

fn main() -> Result<(), serde_yaml::Error> {
    let yaml_str = fs::read_to_string(PATH);
    
    let values: UstxFile = serde_yaml::from_str(&yaml_str.unwrap())?;

    println!("{:#?}", values);

    Ok(())
}

Are you sure you are reading the correct file? Try printing yaml_str before parsing it to check.

Also, note that I have not used the question mark and instead used dbg! on the Result. IMO, this gives more detailed error messages, which I find helpful.

I was accidentally reading a shorter version of the file, but changing that just gave me the same error... Same with dbg!().

short.ustx:

name: New Project
comment: ""
output_dir: Vocal
cache_dir: UCache
ustx_version: "0.6"
resolution: 480
bpm: 120
beat_per_bar: 4

I'm starting to think my code is haunted

Looks like Unable to deserialize jagged array if yml is encoded by UTF8 BOM ยท Issue #224 ยท dtolnay/serde-yaml ยท GitHub

After saving the file as UTF-8 (without BOM), it still has the following issues:

  • UstxFile.version should be String (as already mentioned)
  • UstxFile.expressions should be a HashMap<String, Expression>
  • Expression.flag should be an Option<String>
  • Expression.value_type requires a #[serde(rename = "type")] above it

With these changes I was able to make it work.

You're my savior. God bless

Actually, I think I might be stupid, but it's still giving the "expected a sequence" error after saving in Notepad++ with normal UTF... could you show me your main method? Sorry lol

Here is an updated version of my playground that runs without errors: Rust Playground

2 Likes

I got it working!! Thank you so so much!!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.