Skip to content

Generating YAML and JSON from a YANG data model

When you drive a device with RESTCONF or NETCONF, the payload you send has to match the device's YANG model exactly. This article shows how to read a YANG model and build a correctly structured JSON or YAML body from it — the small, consistent set of rules that decides whether the device accepts your request or rejects it.

Schema versus instance data

A YANG model is a schema. Defined in the YANG 1.1 language (RFC 7950, superseding RFC 6020 for 1.0), it describes the shape of the data: which nodes exist, how they nest, and what type each value has. YANG itself carries no values. The actual configuration — the instance data — is encoded separately as XML, JSON, or YAML.

It is the same relationship a database schema has to a row: the schema says a table has an integer id and a string name; the row supplies 10 and SALES. YANG says an interface has a string name and a boolean enabled; the JSON instance supplies "GigabitEthernet2" and true. Get the mapping right and the body validates cleanly; get it wrong — a list encoded as an object, a boolean wrapped in quotes — and the device rejects it.

The four mappings that cover almost everything

Four YANG node types account for nearly every model you will meet. Learn how each maps to JSON/YAML and you can build any body by walking the model top-down.

YANG node JSON / YAML construct Note
container object / mapping Groups child nodes; has no value of its own
list (has a key) array of objects One object per entry; every entry includes its key leaf
leaf key/value pair The scalar value; its type is respected (see below)
leaf-list array of scalars Multiple values of one type, e.g. [10, 20, 30]

A list is always an array

A YANG list becomes an array even when it has only one entry, and each entry must carry its key leaf. Encoding a single-entry list as a bare object is the most common YANG-to-JSON mistake.

Worked example: ietf-interfaces

Start from the schema. This fragment of the standard ietf-interfaces model defines an interfaces container holding an interface list keyed by name, with name, type, and enabled leaves.

container interfaces {
  list interface {
    key "name";
    leaf name    { type string; }
    leaf type    { type identityref; }
    leaf enabled { type boolean; }
  }
}

Build the JSON by walking that model top-down: the container becomes an object, the list becomes an array, and each leaf becomes a key/value pair. The top-level node is qualified with its module name (ietf-interfaces:interfaces), and because enabled is a boolean leaf its value is an unquoted true.

{
  "ietf-interfaces:interfaces": {
    "interface": [
      {
        "name": "GigabitEthernet2",
        "type": "iana-if-type:ethernetCsmacd",
        "enabled": true
      }
    ]
  }
}

The equivalent YAML holds exactly the same structure — mappings for the containers and a sequence (the leading -) for the list entry:

ietf-interfaces:interfaces:
  interface:
    - name: GigabitEthernet2
      type: iana-if-type:ethernetCsmacd
      enabled: true

Types dictate quoting

A second example makes the type rules concrete. A list of VLANs keyed by id, each with a name leaf, becomes an array of objects — one per VLAN, each carrying its key:

{
  "Cisco-IOS-XE-vlan:vlan": {
    "vlan-list": [
      { "id": 10, "name": "SALES" },
      { "id": 20, "name": "VOICE" }
    ]
  }
}

Notice the data types: id is a number (unquoted 10) and name is a string (quoted "SALES"). The model's leaf types dictate the quoting — you do not choose it freely. Booleans and numbers are unquoted; strings are quoted. "enabled": "true" is a string, not a boolean, and a device that expects a boolean will refuse it.

Namespaces, modules, and RFC 7951

YANG models live in modules, each with a namespace, and the same leaf name can appear in different modules. JSON-encoded YANG data follows RFC 7951 — the application/yang-data+json media type you send to RESTCONF — which resolves the ambiguity by module-qualifying names wherever the module changes.

The rule from RFC 7951 §4 is precise:

  • The top-level node name is always written module-name:node — for example ietf-interfaces:interfaces.
  • A child name is module-qualified only when its module differs from its parent's; otherwise it uses the plain (simple) name.
{
  "ietf-interfaces:interface": [
    {
      "name": "GigabitEthernet1",
      "type": "iana-if-type:ethernetCsmacd",
      "enabled": true
    }
  ]
}

Here interface is module-qualified because it is the top level, name and enabled are bare because they live in the same module, and type's value carries the iana-if-type: prefix because it is an identityref — an enumerable, extensible type whose values are defined in a base identity in a different module. RFC 7951 requires the namespace-qualified form for an identity defined outside the leaf's own module, which is exactly why the interface type is prefixed.

A missing module prefix is a silent 400

RESTCONF validates the body against the model. A structure that does not fit — a list sent as an object, a missing key, a dropped module prefix on the top-level key or on a cross-module value like the interface type — is rejected with 400 Bad Request and no hint about the cause. When a payload you are sure is right is refused, check the structure and the prefixes against the model first.

Three rules that prevent most mistakes

Almost every YANG-to-JSON error is one of these three:

  1. Namespace the top-level node as module:node (e.g. ietf-interfaces:interfaces). Child nodes use plain names unless the module changes.
  2. A list is always an array, even with one entry, and every entry includes its key leaf.
  3. Respect data types — booleans and numbers are unquoted; strings are quoted.

Get these right and your RESTCONF body validates on the first try.

Let tools build the skeleton

Hand-mapping is worth understanding, but you rarely have to start from a blank file.

  • Fetch first, then edit. The fastest way to a valid body is to GET the current object over RESTCONF, then edit the returned JSON in place — the device has already handed you the exact structure it expects.
  • Read the model as a tree. pyang renders a model as an indented tree so you can see the containers, lists, and keys at a glance, and can emit a sample instance skeleton to fill in:

```bash # Show the model's structure pyang -f tree ietf-interfaces.yang

# Emit a sample instance skeleton to edit pyang -f sample-xml-skeleton --sample-xml-skeleton-doctype=data \ -o skeleton.xml ietf-interfaces.yang ```

  • Validate before you send. yanglint (from libyang) parses and validates JSON instance data (RFC 7951) against the schema, so you catch a structural error locally instead of as a 400 from the device:

bash yanglint -t config ietf-interfaces.yang iana-if-type.yang interface.json

Key takeaways

  • YANG is the schema; JSON (RFC 7951), YAML, or XML carry the instance data — the actual values.
  • Map node-by-node: container → object, list → array of objects (each with its key), leaf → key/value, leaf-list → array of scalars.
  • A YANG list is always an array, even with a single entry, and every entry must include its key.
  • The top-level JSON node is namespaced module:node; a child is qualified only when its module differs from its parent's, and an out-of-module identityref value (like an interface type) is qualified too.
  • Respect data types — booleans and numbers unquoted, strings quoted. This is what makes the body validate, and it is exactly the shape RESTCONF expects.
  • Don't hand-build blindly: fetch-then-edit, read the model with pyang -f tree, and validate with yanglint before you send.

Sources: RFC 7950 — The YANG 1.1 Data Modeling Language, RFC 7951 — JSON Encoding of Data Modeled with YANG, pyang, libyang / yanglint.