#keyvalues #vdf #steam #serde


(De)serialize VDF text with serde

3 unstable releases

0.2.1 Dec 24, 2023
0.2.0 Nov 5, 2023
0.1.0 Aug 8, 2021

#1 in #key-values

Download history 739/week @ 2023-11-02 486/week @ 2023-11-09 690/week @ 2023-11-16 786/week @ 2023-11-23 441/week @ 2023-11-30 545/week @ 2023-12-07 685/week @ 2023-12-14 533/week @ 2023-12-21 523/week @ 2023-12-28 593/week @ 2024-01-04 629/week @ 2024-01-11 657/week @ 2024-01-18 649/week @ 2024-01-25 891/week @ 2024-02-01 920/week @ 2024-02-08 647/week @ 2024-02-15

3,210 downloads per month
Used in 14 crates (2 directly)


1.5K SLoC


codecov build status Documentation

keyvalues-serde is a (de)serialization library for VDF text v1 built on the serde framework. This library leverages keyvalues-parser for parsing and rendering the keyvalues text. This makes it easy to deal with VDF text files using strongly typed Rust structures.


Just add the following to your Cargo.toml

keyvalues-serde = "0.1.0"
serde = { version = "1.0.0", features = ["derive"] }


use serde::Deserialize;

// Contents take from my ~/.data/Steam/steam/games/PlatformMenu.vdf
const VDF_TEXT: &str = r##"
// this file defines the contents of the platform menu
            "dll"       "steamui"
            "interface" "SteamUIGames001"
            "MenuName"  "#Steam_Games"
            "SteamApp"  "1"
            "dll"       "bin/friendsui"
            "interface" "VGuiModuleTracker001"
            "MenuName"  "#App_Friends"
            "dll"       "bin/serverbrowser"
            "interface" "VGuiModuleServerBrowser001"
            "MenuName"  "#App_Servers"
            "dll"       "steamui"
            "interface" "VGuiModuleSettings001"
            "MenuName"  "#App_Settings"
            "SteamApp"  "1"

#[derive(Deserialize, Debug)]
struct Platform {
    #[serde(rename = "Menu")]
    menu: Menu,

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Menu {
    games: MenuModule,
    friends: MenuModule,
    servers: MenuModule,
    settings: MenuModule,

#[derive(Deserialize, Debug)]
struct MenuModule {
    dll: String,
    interface: String,
    #[serde(rename = "MenuName")]
    menu_name: String,
    #[serde(rename = "SteamApp")]
    steam_app: Option<bool>,

fn main() -> keyvalues_serde::Result<()> {
    let platform: Platform = keyvalues_serde::from_str(VDF_TEXT)?;
    println!("{:#?}", platform);




  • Primitive Types
    • bool
    • i8, i16, i32, i64, i128
    • u8, u16, u32, u64, u128
    • f32, f64
    • char
  • String
  • Option
    • VDF doesn't have the concept of a null type, so an optional value is considered Some if present and None if missing
  • Unit Variant Enum
    • Represented as text matching the variant name
  • Newtype Struct
    • Considered just a wrapper over the contained data type
  • Homogeneous Sequences (Vec-like types)
    • Represented as several pairs with the same key
  • Heterogeneous Sequences (tuple-like types)
    • Represented as several pairs with the same key
  • TupleStruct
    • Considered a wrapper over the contained tuple
  • Map (HashMap-like types)
    • Represented by a list of pairs contained within curly-braces {}
  • Struct
    • The same as Map. The name of the struct is ignored unless it's the used for the top-level key


Type Reasoning
Byte Array No clear VDF representation
Unit No clear VDF representation
Unit Struct No clear VDF representation
Enum-containers (newtype, tuple, and struct variants) The only clear usage would be the untagged representation in which case the ambiguity of types (everything is essentially just strings or objects) allows for too many footguns for me to be comfortable supporting

Potential Pitfalls

  • Any sequence types containing Options may lead to unexpected ordering issues since a None is just omitted
    • For instance a tuple containing an Option in the middle will be very problematic
  • Empty Vecs and Options with None are both omitted when serializing.
  • Nested sequences are impossible to represent due to the limited nature of sequences in VDF (AFAIK)


Licensed under either of

at your option.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.


~64K SLoC