YAML in powershell
powershell doesn't have native support for yaml. Solution: PSYAML and powershell-yaml
UPDATE on 2018-07-06
I recommend reading my second post A Brief introduction to YAML in Powershell: it's shorter and has less code. It's done after working in a module that's compatible with powershell-yaml and PSYaml modules to read YAML files in Powershell.
I only recommend to read this blog if you're new to YAML and want to see how it works with Powershell. Otherwise, I think the new post is more clear and concise.
No official YAML support in Powershell
YAML is not supported by default in powershell like JSON and XML, although some people are already asking for it in github and Windows user voice (give your voice, too!). YAML is more comfortable for human reading than JSON.
Currently there're two maintained modules to work with YAML:
- PSYaml from Phil Factor. More info in this post. Requires minimum powershell 2.0.
- powershell-yaml from Cloudbase. Requires powershell 3.0 althought it is only tested in 4.0 and 5.0.
Compare PSYAML and powershell-yaml
NOTE: All the code in this post is tested on Powershell 5.0
I wanted to use YAML for a configuration file, so I checked both modules to make a choose. They behaive slighly different. First, I just tested them with a very simple YAML file, without some complex usage that YAML specification enables. Later on, a complex one from Learn x in y minutes. Both read it.
I was wondering the difference between the sequence (having '-') and mappings. So I made a simple YAML document to test it. And how it worked with both modules.
The YAML file is a people's contact list, but each person has their emails written in a different way, to show some of the different options available with YAML format using mappings and sequences. I placed the file in C:\temp\contacts.yaml
.
markus:
name: Mark
surname: Simmon
emails:
personal: mark_@gmail.com
professional: mark.simmon@enterprise.com
johan:
name: Johan
surname: McGuinness
emails:
- personal: johnanmg@hotmail.com
- personal: johnan_mguinness@yahoo.com
- professional: johan@gmail.com
peter_stone:
name: Peter
surname: Stone
emails:
-
address: peter_stone@yahoo.com
type: personal
default: true
-
address: peter@mycompany.com
type: professional
michaelly:
name: Michael
surname: Sunset
emails:
- email:
address: msunset@yahoo.com
type: personal
default: true
- email:
address: michael.sunset@myco.com
type: professional
Each one has a some advantages or inconveniences and they have different behaviour when reading it with Powershell as we'll see when dealing with it powershell module. On YAML file:
- markus: it's the least verbose. Concise. But as mappings are used, only one
professional
and onepersonal
email is possible. - johan: multiple emails of the same type are allowed.
- peter_stone: multiple emails allowed and more parameters possible, for example to set the default email.
- michaelly: the same advantages as
peter_stone
but more verbose.
Depending on the requirements, except michaelly
.
PSYaml
Read the YAML file:
import-module psyaml
[string[]]$fileContent = Get-Content "C:\temp\contacts.yaml"
$content = ''
foreach ($line in $fileContent) { $content = $content + "`n" + $line }
$yaml = ConvertFrom-YAML $content
PSYaml creates OrderedDictionary
for mappings and a System.Array
array for sequences.
In emails
there's an OrderedDictionary
and different ways to access it:
$yaml
<#
Name Value
---- -----
markus {name, surname, emails}
johan {name, surname, emails}
peter_stone {name, surname, emails}
michaelly {name, surname, emails}
#>
$yaml.GetType().FullName
# System.Collections.Specialized.OrderedDictionary
$yaml.markus.emails.getType().FullName
# System.Collections.Specialized.OrderedDictionary
$yaml.markus.emails
<#
Name Value
---- -----
personal mark_@gmail.com
professional mark.simmon@enterprise.com
#>
$yaml.markus.emails.professional
# mark.simmon@enterprise.com
$yaml.markus.emails['professional']
# mark.simmon@enterprise.com
$yaml.markus.emails[1]
# mark.simmon@enterprise.com
johan
$yaml.johan.emails.getType().FullName
# System.Object[]
$yaml.johan.emails
<#
Name Value
---- -----
personal johnanmg@hotmail.com
personal johnan_mguinness@yahoo.com
professional johan@gmail.com
#>
$yaml.johan.emails.personal
# johnanmg@hotmail.com
# johnan_mguinness@yahoo.com
$yaml.johan.emails['personal']
## returns nothing
peter_stone
$yaml.peter_stone.emails.GetType().FullName
# System.Object[]
$yaml.peter_stone.emails
<#
Name Value
---- -----
address peter_stone@yahoo.com
type personal
default True
address peter@mycompany.com
type professional
#>
$yaml.peter_stone.emails.type
# personal
# professional
$yaml.peter_stone.emails.Length
# 2
$yaml.peter_stone.emails.Count
# 2
$yaml.peter_stone.emails[0]
<#
Name Value
---- -----
address peter_stone@yahoo.com
type personal
default True
#>
$yaml.peter_stone.emails[0]['type']
# personal
$yaml.peter_stone.emails[0].type
# personal
michaelly
$yaml.michaelly.emails.GetType().FullName
# System.Object[]
$yaml.michaelly.emails
<#
Name Value
---- -----
email {address, type, default}
email {address, type}
#>
$yaml.michaelly.emails[0].email
<#
Name Value
---- -----
address msunset@yahoo.com
type personal
default True
#>
powershell-yaml
import-module powershell-yaml
[string[]]$fileContent = Get-Content "C:\temp\contacts.yaml"
$content = ''
foreach ($line in $fileContent) { $content = $content + "`n" + $line }
$yaml = ConvertFrom-YAML $content
$yaml
<#
Name Value
---- -----
peter_stone {emails, name, surname}
michaelly {emails, name, surname}
johan {emails, name, surname}
markus {emails, name, surname}
#>
$yaml.GetType().FullName
# System.Collections.Hashtable
The first difference is while PSYaml uses OrderedDictionary
for mappings, powershell-yaml uses Hastable
. That makes that the elements don't follow the same order as the yaml file and that's not possible to access the items by its index.
Added on 2018-06-28: As gaelcolas says, powershell-yaml can keep the order of the YAML document using the -Ordered
parameter. Then it also uses OrderedDictionary
.
$yaml.markus.emails['personal']
# mark_@gmail.com
$yaml.markus.emails.personal
# mark_@gmail.com
$yaml.markus.emails[1]
## returns nothing
When accessing sequences, powershell-yaml uses List
and PSYaml Array
. But there's no difference using them.
$yaml.johan.emails.GetType()
# System.Collections.Generic.List`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
$yaml.johan.emails
<#
Name Value
---- -----
personal johnanmg@hotmail.com
personal johnan_mguinness@yahoo.com
professional johan@gmail.com
#>
$yaml.johan.emails.personal
# johnanmg@hotmail.com
# johnan_mguinness@yahoo.com
$yaml.johan.emails['personal']
## returns nothing
peter_stone
$yaml.peter_stone.emails.GetType().FullName
# System.Collections.Generic.List`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
$yaml.peter_stone.emails
<#
Name Value
---- -----
address peter_stone@yahoo.com
type personal
default True
address peter@mycompany.com
type professional
#>
$yaml.peter_stone.emails.type
# personal
# professional
## This behaives different!!!!
$yaml.peter_stone.emails.Length
# 1
# 1
$yaml.peter_stone.emails.Count
# 2
$yaml.peter_stone.emails[0]
<#
Name Value
---- -----
address peter_stone@yahoo.com
type personal
default True
#>
$yaml.peter_stone.emails[0]['type']
# personal
$yaml.peter_stone.emails[0].type
# personal
Learn x in y minutes
Both modules read the complex Learn x in y minutes example successfully:
import-module powershell-yaml
[string[]]$fileContent = Get-Content "C:\temp\learnxinyminutes.yaml"
$content = ''
foreach ($line in $fileContent) { $content = $content + "`n" + $line }
$yaml = ConvertFrom-YAML $content
import-module PSYaml
[string[]]$fileContent = Get-Content "C:\temp\learnxinyminutes.yaml"
$content = ''
foreach ($line in $fileContent) { $content = $content + "`n" + $line }
$yaml = ConvertFrom-YAML $content
Conclusion
Althought internally the modules use different objects' types. If they're carefully used, they can have the same behaviour, except that powershell-yaml doesn't mantain the order that exists in yaml file in the mappings (althought it maintains it in the sequences). For me it's an important think.
As a resume:
module | mappings | sequences |
---|---|---|
PSYaml | OrderedDictionary | Array |
powershell-yaml | Hastable | List |
I wish Powershell native support for YAML in future releases.
YAML resources
- YAML homepage is a YAML itself. Amazing :D
- YAML specification is always an option, but I feel more confortable reading some example before.
- Learn X in Y minutes has a nice example showing all the powerfull options of YAML in a very pleasant way.