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:
- Provide a sample chunk of YAML to demonstrate LSTs
- Discuss how that YAML relates to different types of LSTs
- Teach you how to learn more about YAML LSTs yourself
{% hint style="info" %} 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. {% endhint %}
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
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.
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 ---
.
A Mapping consists of 1 or more Mapping Entries (key-value pairs). Most YAML files will contain at least one Mapping.
{% hint style="info" %}
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.
{% endhint %}
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.
A Sequence is an ordered list of 1 or more Sequence Entries.
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.
A Scalar is a YAML value such as a string, a number, or a boolean.
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: