Why YAML is better than JSON (read before screaming)
Tags: yaml, json, programming, webdev
Published: 2023-03-17 09:20:10

<!-- Description of the YAML format -->
You have probably seen or used the YAML format in configuration files.
YAML (a recursive acronym for “YAML Ain’t Markup Language”) is a human-friendly data serialization language for all programming languages, like the JSON format.

<!-- YAML example -->
The YAML files are mostly written using the Python-style indentation to indicate nesting such as the following example of a [Dofigen](https://github.com/lenra-io/dofigen) file:
```yaml
# An anchor
from: &image "docker.io/bitnami/node:18"
# A string field
workdir: /app
# An object list
builders:
  # An object
  - name: module-loader
    from: *image
    workdir: /tmp/module
    adds:
      - package.json
      - package-lock.json
    script:
      - npm i --production --cache /tmp/cache
    caches:
      - /tmp/cache
  - name: builder
    from: *image
    workdir: /tmp/app
    adds:
      - .
    script:
      - npm i --cache /tmp/cache
      - npm run build
    caches:
      - /tmp/cache
artifacts:
  - builder: module-loader
    source: /tmp/module/
    destination: "."
  - builder: builder
    source: /tmp/app/dist/
    destination: dist/
  - builder: builder
    source: /tmp/app/resources/
    destination: resources/
# A string list
cmd:
  - npm
  - start
# An integer list
ports:
  - 3000
ignores:
- "**"
- "!/*.json"
- "!/src/"
- "!/resources/"
```

<!-- Transition: you think that a format can't be better than another one, they are differents -->
You may say: 
> OK, the YAML is great and very readable but how could it be **better** than JSON ?
> They are just two different formats.

That's true, but I will show you how YAML is at least as good as JSON.

## YAML is at least as good as JSON
<!-- Steps to make the previous example compatible with JSON -->
The YAML format permits many ways to define the data.
We will see here the different ways:
- [strings](#strings)
- [arrays/list](#arrayslists)
- [objects/strutures](#objectsstrutures)


### strings

The basic string description is just writing the string:
```yaml
myString: My super string
```

In order to disambiguate other data types like numbers or booleans, we can define the string by surrounding the value with simple quotes(`'`) or double quotes(`"`):
```yaml
myString: "My super string"
alsoString: '10'
stringNotBoolean: "true"
```


For multiline strings we also can use two specific descriptions:
```yaml
myString: >
  My super 
  multiline string
  
  Second line
otherString: |
  The other super multiline string
  Second line
```

Look at [this website](https://yaml-multiline.info/) to read more about it.

### arrays/lists

The main array description in YAML is the next one:
```yaml
myArray:
- My string value
- 42
```

But YAML also permits the use of another syntax, by surrounding the array elements with brackets (`[` and `]`), very useful for short or empty arrays:
```yaml
myShortArray: [first, second]
emptyArray: []
```

### objects/structures

The main object description in YAML is the following one:
```yaml
name: My string
age: 24
subobject: 
  nestedField: The value
```

That would result to the next JSON object:
```json
{
  "name": "My string",
  "age": 24,
  "subobject": {
    "nestedField": "The value"
  }
}
```

But the YAML format also permits the use of braces (`{` and `}`) to surround the object fields and they can be separated by a comma+space instead of a new line.
It also permits the use of simple quotes(`'`) or double quotes(`"`) arround the field name.
Here is the same object with this syntax:
```yaml
{
  'name': My string,
  "age": 24,
  subobject: { nestedField: The value }
}
```

### Merged YAML secondary format

Do you see where I'm going with this ?

Here is another way to describe the initial YAML example, but using the syntaxes seen previously:

```yaml
{
  # An anchor
  "from": &image "docker.io/bitnami/node:18",
  # A string field
  "workdir": "/app",
  # An object list
  "builders": [
    # An object
    {
      "name": "module-loader",
      "from": *image,
      "workdir": "/tmp/module",
      "adds": [
        "package.json",
        "package-lock.json"
      ],
      "script": [ "npm i --production --cache /tmp/cache" ],
      "caches": [ "/tmp/cache" ]
    },
    {
      "name": "builder",
      "from": *image,
      "workdir": "/tmp/app",
      "adds": [ "." ],
      "script": [
        "npm i --cache /tmp/cache",
        "npm run build"
      ],
      "caches": [ "/tmp/cache" ]
    }
  ],
  "artifacts": [
    {
      "builder": "module-loader",
      "source": "/tmp/module/",
      "destination": "."
    },
    {
      "builder": "builder",
      "source": "/tmp/app/dist/",
      "destination": "dist/"
    },
    {
      "builder": "builder",
      "source": "/tmp/app/resources/",
      "destination": "resources/"
    }
  ],
  # A string list
  "cmd": [ "npm", "start" ],
  # An integer list
  "ports": [ 3000 ],
  "ignores": [
    "**",
    "!/*.json",
    "!/src/",
    "!/resources/"
  ]
}
```

Does it look familiar ?
Yes, the YAML format is fully compatible with JSON data.

But there are still some elements that you can't find in JSON format.

## YAML additional features
<!-- List of YAML features not managed by JSON -->
The YAML format also has many interesting additional features, let's take a look at some of those I use in my projects.

### Comments

One of the features that I miss the most in JSON files is the comments.
How many times I tried to comment a dependency in a `package.json` file or a configuration...

In YAML, you can comment a line by just adding a hashtag (`#`) before the line content.
So simple !

```yaml
# my comment
```

### Anchors

YAML format also lets you avoid repeating data in your configuration files thanks to the anchors.
With this feature you can define an anchor (`&`) by setting its name before a value (of any type).
You can then use an alias (`*`) as value of another field (later in the same YAML file).


The anchors let you change a value used many times at a single point like from Docker image in the initial example:
```yaml
# An anchor
from: &image "docker.io/bitnami/node:18"
...
builders:
  # An object
  - name: module-loader
    from: *image
...
```

This is even more useful for objects and arrays.

YAML anchors also let you extend and override an object for a new value by entering `<<:` before the alias.
Here is an example with a builder:

```yaml
&base
from: "docker.io/bitnami/node:18"
workdir: /app
builders:
  - <<: *base
    name: module-loader # extension to add the name
    workdir: /tmp/module # override the workdir
    adds:
      - package.json
      - package-lock.json
    script:
      - npm i --production --cache /tmp/cache
    caches:
      - /tmp/cache
    builders: # override the build to null to avoid circular references
```

This feature is also very useful but could lead to less readable files if not done wisely.

## Conclusion

We have seen previously that the YAML format is fully compatible with the JSON one and that it has many additionnal features, but I wrote this article focusing on the human-friendly part of those languages.
This is not the only important aspect for a format.
The permissivity of the YAML format makes it more readable and easy to use (at least to me ^^), but it also can make it less efficient to process in a program...

To look for all the additionnal features see the [full specification (at the current date)](https://yaml.org/spec/1.2.2/).

Sources:
- https://en.wikipedia.org/wiki/YAML
- https://yaml.org/
- https://www.educative.io/blog/advanced-yaml-syntax-cheatsheet
- https://yaml-multiline.info/