Some fixes to YAML post

This commit is contained in:
Thomas Lovén 2018-08-28 10:47:54 +02:00
parent 8166afd667
commit b43802b396

View File

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