YAML LST examples

When building recipes that read or modify YAML files, it's important to understand how the YAML Lossless Semantic Tree (LST) is constructed.

To help you with that, this guide will:

If you want a more thorough explanation of what LSTs are and how they're used in recipes, please read this high-level LST explanation.

Sample YAML

Below is a simple YAML file whose entire purpose is to demonstrate different types of LSTs. Each of the following sections will highlight different parts of this file to demonstrate which chunks correspond to which LST. This listing of LSTs is not exhaustive, but it should give you a good sense of the most common types.

document: this is document 1

jedis-list:
  - Yoda
  - Qui-Gon Jinn
  - Obi-Wan Kenobi
  - Luke Skywalker

jedi:
  name: Obi-Wan Kenobi
  home-planet: Stewjon
  height: 1.82m

requests:
  - http://example.com/
  - url: http://example.com/
    method: GET
---
document: this is document 2

reporting:
- module: final-stats
- module: console
---
- item_1
- item_2

YAML LSTs

Documents

The Documents LST is the root of the YAML LST. In order for an LST to represent valid YAML, all other elements must be contained inside of this. It is composed of one or more Document LSTs.

Document

The Document LST contains all of the YAML in a single document. A YAML file can have more than one document - with each document being separated by a line containing the triple-dash separator ---.

Mapping

A Mapping consists of 1 or more Mapping Entries (key-value pairs). Most YAML files will contain at least one Mapping.

The module lines in the reporting section of the second document are not part of the same Mapping as each line is a Sequence Entry. In contrast, the url and method lines in the requests section of the first document are part of the same mapping as, together, they make up one Sequence Entry.

Mapping Entry

A Mapping Entry is a key-value pair. The key is usually a Scalar. The value, on the other hand, can be most LSTs such as a Mapping, a Sequence, or a Scalar.

For example, the second entry in the first doc (jedis-list) has a Scalar key. The value for that entry is a Sequence that contains a list of all the Jedi.

Sequence

A Sequence is an ordered list of 1 or more Sequence Entries.

Sequence Entry

A Sequence Entry is one item in a Sequence. You can think of each sequence entry as the value in a key-value pair. This value can be most other LSTs, such as a Mapping or a Scalar. Unlike Mapping Entries, Sequence Entries do not have a key.

Scalar

A Scalar is a YAML value such as a string, a number, or a boolean.

Using the debugger to detect LSTs

If you find yourself still unsure what makes up a particular LST or if you want to traverse the LST yourself, you can use the Java debugger to help you.

To do so, make a simple recipe that uses the YamlIsoVisitor:

package com.yourorg;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.yaml.YamlIsoVisitor;
import org.openrewrite.yaml.tree.Yaml;

public class TestYaml extends Recipe {
    @Override
    public String getDisplayName() {
        return "test";
    }

    @Override
    public String getDescription() {
        return "test test.";
    }

    @Override
    protected YamlIsoVisitor<ExecutionContext> getVisitor() {
        return new YamlIsoVisitor<ExecutionContext>() {
            @Override
            public Yaml.Documents visitDocuments(Yaml.Documents documents, ExecutionContext executionContext) {
                // Add a breakpoint on the next line
                return super.visitDocuments(documents, executionContext);
            }
        };
    }
}

Next, make a simple test that contains the YAML you want to learn more about:

package com.yourorg;

import org.junit.jupiter.api.Test;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.yaml.Assertions.yaml;

public class TestYamlTest implements RewriteTest {

    @Override
    public void defaults(RecipeSpec spec) {
        spec.recipe(new TestYaml());
    }

    @Test
    void debugTest() {
        rewriteRun(
            yaml(
                """
                    document: this is document 1
                    
                    jedis-list:
                    - Yoda
                    - Qui-Gon Jinn
                    - Obi-Wan Kenobi
                    - Luke Skywalker
                    
                    jedi:
                    name: Obi-Wan Kenobi
                    home-planet: Stewjon
                    height: 1.82m
                    
                    requests:
                    - http://example.com/
                    - url: http://example.com/
                        method: GET
                    ---
                    document: this is document 2
                    
                    reporting:
                    - module: final-stats
                    - module: console
                    ---
                    - item_1
                    - item_2
                """
            )
        );
    }
}

With those created, you can add a breakpoint inside of the overridden visitDocuments() method in your recipe. Then, when you debug your recipe, you'll be able to see the entire LST and traverse through it as you desire:

Last updated