JSON Config

Parse JSON, stringify it, and model configuration data with a typed struct.

1

Core concepts

std.json, json.parse, json.stringify, struct config, project layout

2

How the learner should approach this project

Do not paste the complete program immediately. Create the file, type the first step, run vpp check, and then run the program when a complete entry point exists. Read the explanation before looking at the snippet. After each step, predict what the new code should do. This turns the page into a lesson rather than a code dump.

3

Step 1: Import std.json

The standard JSON module provides parse and stringify operations. Imports must appear before functions and types.

After typing the snippet, identify the new names introduced by the step. Ask what each name represents, what type the compiler should infer or check, and what value should exist after the code runs.

main.vpp
import std.json
4

Step 2: Define AppConfig

The struct gives the configuration a typed shape. name and version are strings, while debug is a boolean. This is the point where unstructured configuration data becomes a clearly named application model.

Before moving forward, explain how this step connects to the previous one. In particular, identify which values cross a function boundary, which values change, and which values are guaranteed by the type system.

main.vpp
struct AppConfig {
    name: string
    version: string
    debug: bool
}
5

Step 3: Parse and stringify JSON

parse receives raw JSON text and produces a JSON document value. stringify converts that document back into text.

This function demonstrates a clean boundary between raw configuration text and structured processing.

Before moving forward, explain how this step connects to the previous one. In particular, identify which values cross a function boundary, which values change, and which values are guaranteed by the type system.

main.vpp
fn parse_config(raw: string) -> string {
    let doc = json.parse(raw)
    return json.stringify(doc)
}
6

Step 4: Use JSON and typed configuration together

main starts with a JSON string containing name, version, and debug. It passes the raw text through parse_config and prints the resulting JSON. It then creates a typed AppConfig value and accesses its fields directly.

Before moving forward, explain how this step connects to the previous one. In particular, identify which values cross a function boundary, which values change, and which values are guaranteed by the type system.

main.vpp
fn main() -> int {
    let raw = "{"name":"v++","version":"1.0.0","debug":false}"
    print(parse_config(raw))
    let cfg = AppConfig {
        name: "v++"
        version: "1.0.0"
        debug: true
    }
    print(cfg.name)
    print(cfg.version)
    return 0
}
7

Complete source

main.vpp
import std.json
struct AppConfig {
    name: string
    version: string
    debug: bool
}
fn parse_config(raw: string) -> string {
    let doc = json.parse(raw)
    return json.stringify(doc)
}
fn main() -> int {
    let raw = "{"name":"v++","version":"1.0.0","debug":false}"
    print(parse_config(raw))
    let cfg = AppConfig {
        name: "v++"
        version: "1.0.0"
        debug: true
    }
    print(cfg.name)
    print(cfg.version)
    return 0
}
8

Expected behavior

The complete program should produce the following output when run with the command shown below. Exact formatting should follow the current V++ runtime.

{"name":"v++","version":"1.0.0","debug":false}
v++
1.0.0
9

Run the project

terminal
vpp run projects/20-json-config/main.vpp
10

What the learner should understand after this project

JSON is unstructured text at the boundary, while a struct provides a typed model inside the program. Keeping those roles separate makes configuration easier to reason about.

11

Common mistakes to teach

  1. Malformed JSON text
  2. Using a JSON field as if it were already a typed struct field
  3. Changing the struct without updating construction sites
12

Practice extension

Add a port field to AppConfig as an int and update the example configuration.

A strong learner should be able to explain the program without looking at the code, rebuild the core idea from memory, and make the practice change without copying a solution.

13

Full program

main.vpp
import std.json
struct AppConfig {
    name: string
    version: string
    debug: bool
}
fn parse_config(raw: string) -> string {
    let doc = json.parse(raw)
    return json.stringify(doc)
}
fn main() -> int {
    let raw = "{"name":"v++","version":"1.0.0","debug":false}"
    print(parse_config(raw))
    let cfg = AppConfig {
        name: "v++"
        version: "1.0.0"
        debug: true
    }
    print(cfg.name)
    print(cfg.version)
    return 0
}
vpp
$ ready. Click Test program.