Some fixes to YAML post
This commit is contained in:
parent
8166afd667
commit
b43802b396
@ -12,22 +12,21 @@ great developers and a great community. I've been hanging about the official
|
||||
[Discord Chat Server](https://discord.gg/c5DvZ4e), and try to give something
|
||||
back by helping people when I can.
|
||||
|
||||
One thing I noticed is that people often struggle with is the fact that the
|
||||
configuration is made through YAML. A strange choice for the kind of
|
||||
quasi-programming you may want to do when automating your home appliances.
|
||||
One thing I noticed that people often struggle with is the YAML configuration
|
||||
files. A strange choice for the kind of quasi-programming you may want to do
|
||||
when automating your home appliances.
|
||||
|
||||
In this post I have tried to describe how YAML works, and how I think about the
|
||||
way it represents basic data structures. I hope it can be useful to someone...
|
||||
|
||||
### Dictionaries and lists
|
||||
|
||||
To understand YAML, you need to understand what it's describing. There are two
|
||||
main concepts: *Dictionaries* and *Lists*.
|
||||
To understand YAML, you need to understand what it's describing. YAML works
|
||||
with two main data structures - *Dictionaries* and *Lists*.
|
||||
|
||||
|
||||
**Lists** are quite simple. It's just a list - an ordered collection of things.
|
||||
There are two things you need to remember though. Let me show them to you in a
|
||||
list :)
|
||||
**Lists** are easy to understand. They're just a list - ordered collections of
|
||||
things. There are two things you need to remember though. Let me show them to
|
||||
you in a list :)
|
||||
|
||||
- Lists are ordered. If you make a list with a dog, a cat and an elephant, the
|
||||
dog will be first, the elephant will be last and the cat will be in between.
|
||||
@ -42,17 +41,20 @@ your home.
|
||||
- A list of things to pack for the vacation next week
|
||||
- The list of passwords on a post-it under your keyboard
|
||||
|
||||
**Dictionaries** are not much more complicated. You've probably seen it in some
|
||||
way or another, but perhaps with a different name. In different programming
|
||||
languages they can be called *dictionaries*, *hashes*, *maps*, *hash tables*,
|
||||
*tables*, *collections* or even just *objects*. The technical name is
|
||||
*Associative Array*.
|
||||
**Dictionaries** (or *dicts* for short) are not much more complicated. You've
|
||||
probably encountered them in some way or another, but perhaps with a different
|
||||
name. In different programming languages they can be called *dictionaries*,
|
||||
*hashes*, *maps*, *hash tables*, *tables*, *collections* or even just
|
||||
*objects*. The technical name is *Associative Array*.
|
||||
|
||||
Regardless of name, the concept is simple. A dictionary is a collection of
|
||||
key-value pairs. That is, a Name for something, and that Something. The name -
|
||||
the *key* must be unique. The same key can not be used twice in the same
|
||||
dictionary. The Something - the *value* can be anything at all, just like the
|
||||
items in a list.
|
||||
Regardless of name, the concept is simple. A dictionary is an unordered
|
||||
collection of entries - where each entry has a *Key* and a *Value*.
|
||||
The Key can be thought of as a name or title for the entry, and the Value is
|
||||
the data of the entry. Remember:
|
||||
|
||||
- Keys are unique. Two entries in the same dict can not have the same key.
|
||||
- Values can be anything, just like the items in a list - Strings, integers,
|
||||
booleans, dictionaries or lists.
|
||||
|
||||
Let's look at some sample dictionaries:
|
||||
|
||||
@ -64,9 +66,9 @@ Thursday: Pea soup and pancakes
|
||||
Friday: Pizza
|
||||
```
|
||||
|
||||
Each day is labeled by a key, and has a value - what are you going to eat that day.
|
||||
Note that while you could add another `Wednesday` to the end of the list, it
|
||||
wouldn't really make sense. Thus keys have to be unique. The values doesn't
|
||||
Each day is labeled by a key, and has a value - what are you going to eat that
|
||||
day. Note that while you could add another `Wednesday` to the end of the list,
|
||||
it wouldn't really make sense. Thus keys have to be unique. The values doesn't
|
||||
however. It would make perfect sense to eat pizza again on Saturday.
|
||||
|
||||
Since keys are unique, their order is not important:
|
||||
@ -89,13 +91,13 @@ Name: Thomas Lovén
|
||||
Email: thomasloven@example.com
|
||||
```
|
||||
|
||||
That's a dictionary. Looks kind of like a database of a sort, doesn't it?
|
||||
Like the address book in your email program? Ah! But don't get fooled. The
|
||||
address book is a **list**, not a **dictionary**. However - the *items* in the
|
||||
list are *dictionaries*.
|
||||
That's a dictionary. Looks kind of like a database of a sort, doesn't it? Like
|
||||
the address book in your email program? Ah! But don't get fooled. The address
|
||||
book is a **list**, not a **dictionary**. However - the *items* in the list are
|
||||
*dictionaries*.
|
||||
|
||||
|
||||
Let's add on to that dictionary:
|
||||
Let's add on to that dict:
|
||||
|
||||
```yaml
|
||||
Name: Thomas Lovén
|
||||
@ -103,9 +105,9 @@ Email: thomasloven@example.com
|
||||
Hobbies: singing, woodworking, home automation
|
||||
```
|
||||
|
||||
Now we added an entry to the dictionary where the value is a list. I have three
|
||||
hobbies. This illustrates that the value of a dictionary key-value pair can be
|
||||
anything. Even a dictionary:
|
||||
Now we added an entry to the dict where the value is a list. I have three
|
||||
hobbies. This illustrates that the value of a dictionary entry can be anything.
|
||||
Even a dictionary:
|
||||
|
||||
```yaml
|
||||
Name: Thomas Lovén
|
||||
@ -124,8 +126,8 @@ Phones: Home: +46 (0)XX XXXXXX, Work: +40 (0)XX XXXXXX
|
||||
Children: Name: N, Age: 3 ; Name: H, Age: 1
|
||||
```
|
||||
|
||||
But that this point things are getting advanced. It's hard to keep track of
|
||||
what is a dictionary and what is a list and what contains what...
|
||||
But at this point things are getting advanced. It's hard to keep track of what
|
||||
is a dict and what is a list and what contains what...
|
||||
|
||||
If only there was a language to describe these concepts... a sort of Markup
|
||||
Language, if you will... but who needs Yet Another one of those?
|
||||
@ -135,23 +137,23 @@ Let me tell you about
|
||||
### JSON
|
||||
|
||||
Javascript Structured Object Notation - [JSON](https://www.json.org/). You
|
||||
thought I was going to say YAML, didn't you? We'll get there too.
|
||||
thought I was going to say YAML, didn't you? Soon...
|
||||
|
||||
JSON is a simple way of writing down the concepts described above which can be
|
||||
JSON is a simple way of writing down the concepts described above, which can be
|
||||
easily understood both by humans and by computers.
|
||||
|
||||
Basically, there are *objects* and *arrays*, but let's call them
|
||||
*dictionaries* and *lists* instead.
|
||||
JSON describes *object* and *arrays*, there are *objects* and *arrays*, but
|
||||
let's call them *dictionaries* and *lists* instead.
|
||||
|
||||
*lists* are surrounded by square brackets and contain items separated by commas.
|
||||
The items can be strings, numbers, dictionaries, lists or any of the magic
|
||||
values `true`, `false`, or `null`.
|
||||
|
||||
*Dictionaries* are surrounded by curly braces and contain key-value pairs
|
||||
separated by commas. Each key-value pair has the key, a colon and the value.
|
||||
Keys must be strings, but values can be anything that can be in a list.
|
||||
*Dictionaries* are surrounded by curly braces and contain entries separated by
|
||||
commas. The key and value of each entry is separated by a colon. Keys must be
|
||||
strings, but values can be anything that can be in a list.
|
||||
|
||||
Let's look at our dictionary from above in JSON format:
|
||||
Let's look at our dict from above in JSON format:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -174,12 +176,12 @@ Let's look at our dictionary from above in JSON format:
|
||||
```
|
||||
|
||||
I added some line breaks and indentations to make it more pretty, but this is
|
||||
much easier to read. Even the last key-value pair about my children.
|
||||
Two things to note
|
||||
much easier to read. Even the last entry with the nested dict-list-dict about
|
||||
my children. Two things to note:
|
||||
|
||||
- You don't need quotes around the keys, but you do around values that are
|
||||
strings. If you want whitespace in a key (which is entirely OK) it must be
|
||||
quoted, though.
|
||||
- You don't need quotes around the keys, but you do around string values. If
|
||||
you want whitespace in a key (which is entirely OK) it must be quoted,
|
||||
though.
|
||||
- The indentations and newlines I added, and in fact any whitespace not in
|
||||
quotes, is ignored.
|
||||
|
||||
@ -190,11 +192,11 @@ OK. Now you understand one markup language. Let's learn something different.
|
||||
[YAML](http://yaml.org/) Ain't a Markup Language - but it's pretty darn close,
|
||||
to be honest.
|
||||
|
||||
While probably not historically accurate, YAML can be seen as an evolution of
|
||||
JSON. In fact, any valid JSON is also valid YAML. That might be important to
|
||||
remember. There are some notable differences, though.
|
||||
While probably not historically accurate, YAML can be seen as an evolution or
|
||||
superset of JSON. In fact, any valid JSON is also valid YAML. That might be
|
||||
useful to remember. There are some notable differences, though.
|
||||
|
||||
First of all, YAML does away with the braces. Instead items in lists are
|
||||
First of all, YAML doesn't require braces. Instead items in lists are
|
||||
separated by newlines where each item starts with a dash:
|
||||
|
||||
```yaml
|
||||
@ -217,13 +219,19 @@ Some things to note:
|
||||
blessing and a curse. For example `"true"` is a string, but `true` is a
|
||||
boolean value.
|
||||
- Indentation is important. Item 3 in the list stretches over multiple lines.
|
||||
Each line after the first one is indented (with an equal number of spaces,
|
||||
Each line after the first one is indented (with an equal number of spaces -
|
||||
*NOT* tabs). The same is true for Item 5, which is a list. Each item in the
|
||||
sub-list is indented with an equal number of spaced.
|
||||
- The items of the list are not of the same type. Most are strings, but item 5
|
||||
is a list.
|
||||
|
||||
Dictionaries are also separated on lines with the key, a colon and the value:
|
||||
I'll just take the opportunity to say this again: Indentation is important.
|
||||
Probably the most important concept of YAML.
|
||||
|
||||
Slightly simplified one can say that by indenting line, those line become a
|
||||
continuation of the line above, until the next de-dented line.
|
||||
|
||||
Dictionaries are also written one entry per line, as `key: value`:
|
||||
|
||||
```yaml
|
||||
Name: Thomas Lovén
|
||||
@ -245,12 +253,11 @@ Children:
|
||||
|
||||
Things to note:
|
||||
|
||||
- The value corresponding to the key `Hobbies` is a list. Like above, each line
|
||||
of the value is indented by an equal number of spaces.
|
||||
- The value corresponding to the key `Phones` is a dictionary. The same
|
||||
indentation rules apply.
|
||||
- The value corresponding to the key "Children" is a list where each item is a
|
||||
dictionary. So each line in each dictionary is indented twice.
|
||||
- The value of `Hobbies` is a list. Like above, each line of the value is
|
||||
indented by an equal number of spaces.
|
||||
- The value of `Phones` is a dictionary. The same indentation rules apply.
|
||||
- The value of `Children` is a list where each item is a dictionary. So each
|
||||
line in each dictionary is indented twice.
|
||||
- The second entry in the list of children uses a contracted form, where the
|
||||
first key-value pair of the dictionary is put on the same line that signifies
|
||||
the list item. More on this later.
|
||||
@ -265,19 +272,20 @@ any other. Indentation errors.
|
||||
|
||||
*Indentation is important*
|
||||
|
||||
It *must* be correct, or the YAML won't be accepted by the parser, or it will
|
||||
It *must* be correct or the YAML either won't be accepted by the parser or will
|
||||
describe something entirely different from what you intended.
|
||||
|
||||
The only advice I can give is to think carefully about the structure of the
|
||||
data you are trying to represent. What is your object? Is it a dictionary or a
|
||||
list? Where is it contained? Is it freestanding? Is it the value of a
|
||||
dictionary key-value pair? Is it an item in a list? What is it's parent? What
|
||||
are it's children? What are it's siblings?
|
||||
list? Where is it contained? Is it freestanding? Is it an entry in a dict? Is
|
||||
it an item in a list? What is it's parent? What are it's children? What are
|
||||
it's siblings?
|
||||
|
||||
It it's a complex structure, it might help to make a drawing on actual paper.
|
||||
If you have a complex structure - say some nested cards in lovelace - it might
|
||||
help to make a drawing on actual paper of what you want.
|
||||
|
||||
In the YAML dictionary sample above, I used a contracted for in my list of
|
||||
dictionaries. This is common practice, but may be a bit confusing at first
|
||||
In the YAML dictionary sample above, I used a contracted form in my list of
|
||||
dicts. This is common practice, but may be a bit confusing at first
|
||||
since it makes the indentation unclear.
|
||||
|
||||
If might be easier to understand the structure of the document if you use the
|
||||
@ -293,15 +301,32 @@ Children:
|
||||
Age: 1
|
||||
```
|
||||
|
||||
Also, remember that dictionary entries are unordered. The YAML below describes
|
||||
the same thing as that above, but might be more confusing.
|
||||
|
||||
```yaml
|
||||
Children:
|
||||
- Name: N
|
||||
Age: 3
|
||||
- Age: 1
|
||||
Name: H
|
||||
```
|
||||
|
||||
Often software generated YAML (such as what's produced by
|
||||
[lovelace-gen](https://github.com/thomasloven/homeassistant-lovelace-gen))
|
||||
write the entries out in such a way that the keys are ordered alphabetically.
|
||||
|
||||
### Advanced topics
|
||||
|
||||
#### Comments
|
||||
|
||||
Adding comments to your code makes it easier to understand. Both to other
|
||||
people, and - more importantly - to you when you return to it in six months
|
||||
because something stopped working.
|
||||
people, and - more importantly - to your self when you return to it in six
|
||||
months because something stopped working.
|
||||
|
||||
In YAML, comments begin with a number sign `#`, last until the end of the line
|
||||
and are ignored by the parser.
|
||||
and are simply ignored by the parser.
|
||||
|
||||
```yaml
|
||||
# A dictionary about me
|
||||
Name: Thomas Lovén
|
||||
@ -312,6 +337,8 @@ Hobbies:
|
||||
- woodworking
|
||||
```
|
||||
|
||||
If you want a number sign in your entry, the entry must be quoted.
|
||||
|
||||
#### Spaces and colons
|
||||
|
||||
As mentioned, YAML doesn't require quotes around strings, but they are allowed.
|
||||
@ -323,8 +350,9 @@ Quotes can be useful to tweak the parsing. Imagine for example the following lis
|
||||
- Halflife 2: Episode Two
|
||||
```
|
||||
|
||||
This is a list of strings, right? Wrong. The third entry is a dictionary with
|
||||
the key "Halflife 2" and the value "Episode Two" (keys can contain spaces, by the way).
|
||||
This is a list of strings, right? Wrong. The third entry is a dictionary with a
|
||||
single entry "Halflife 2" with value "Episode Two" (keys can contain spaces, by
|
||||
the way).
|
||||
|
||||
To fix this, you can use quotes:
|
||||
```yaml
|
||||
@ -357,9 +385,17 @@ and make the data hard to read instead. In the end it's a matter of taste.
|
||||
Note that there are still no quotes. That's OK as long as you don't want
|
||||
commas, } or ] in the value.
|
||||
|
||||
Further, YAML allows single line comma separated lists without braces.
|
||||
|
||||
```yaml
|
||||
Hobbies: singing, woodworking, home automation
|
||||
```
|
||||
|
||||
This also means that a string containing a comma must be quoted.
|
||||
|
||||
#### Merging
|
||||
|
||||
Dictionaries can be merged using the key: `<<`. For example:
|
||||
Dictionaries can be merged using the merging operator: `<<`. For example:
|
||||
|
||||
```yaml
|
||||
a key: a value
|
||||
@ -384,8 +420,8 @@ b key: b value
|
||||
d key: d value
|
||||
```
|
||||
|
||||
In short, the `<<` key takes a dictionary as its value, and merges it into the
|
||||
parent dictionary.
|
||||
In short, the `<<` operator takes a dictionary as its value, and merges it into
|
||||
the parent dictionary.
|
||||
|
||||
|
||||
#### Node anchors
|
||||
@ -401,8 +437,8 @@ my_dict: &my_dict
|
||||
c: 3
|
||||
```
|
||||
|
||||
In this case `&my_dict` is NOT the value corresponding to the key `my_dict`,
|
||||
but a node anchor - as signified by the ampersand `&`.
|
||||
In this case `&my_dict` is NOT the value of `my_dict`, but a node
|
||||
anchor - as signified by the ampersand `&`.
|
||||
|
||||
The anchor saves the value for later reuse and can be recalled any number of
|
||||
times using an asterisk `*`:
|
||||
@ -456,13 +492,14 @@ extended version:
|
||||
c: 3
|
||||
```
|
||||
|
||||
At this point, understanding how this will parse shouldn't be a problem to you.
|
||||
You should be able to guess how this parses out.
|
||||
|
||||
Now, for my final trick:
|
||||
|
||||
#### Merging while defining.
|
||||
|
||||
The problem with the above examples is that you need to put the definition
|
||||
somewhere. The YAML snippets above will have the dictionary keys `a
|
||||
somewhere. The YAML snippets above will have the dictionary entries `a
|
||||
dictionary`and `base` defined and set no matter what. Sometimes that's
|
||||
impractical, which is why you often see the following in homeassistant
|
||||
packages:
|
||||
@ -483,7 +520,7 @@ homeassistant:
|
||||
icon: mdi:home
|
||||
```
|
||||
|
||||
The `package.node_anchors` key in the `customize` dictionary contains a
|
||||
The `package.node_anchors` entry in the `customize` dictionary contains a
|
||||
dictionary of stuff that is simply ignored. Anything you put there will have no
|
||||
effect on the package, so it's a great place to define anchors.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user