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 exampleietf-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:
- Namespace the top-level node as
module:node(e.g.ietf-interfaces:interfaces). Child nodes use plain names unless the module changes. - A list is always an array, even with one entry, and every entry includes its key leaf.
- 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
GETthe 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.
pyangrenders 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 a400from 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 interfacetype) 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 withyanglintbefore you send.
Sources: RFC 7950 — The YANG 1.1 Data Modeling Language, RFC 7951 — JSON Encoding of Data Modeled with YANG, pyang, libyang / yanglint.