Conversion¶
Convert between YAML text and PowerShell values.
| Command | Purpose |
|---|---|
ConvertFrom-Yaml |
Parse one or more YAML documents into PowerShell values. |
ConvertTo-Yaml |
Serialize supported PowerShell values as YAML 1.2-compatible text. |
Conversion projects YAML through to PowerShell values: a parsed document becomes objects, arrays and scalars you can index, filter and pass down the pipeline, and a PowerShell value becomes YAML text. If you need to keep YAML as YAML, with its tags, anchors and node kinds intact, use the Streams commands instead.
Parse YAML¶
Ordinary string-key mappings become ordered PSCustomObject values. A top-level
sequence writes its items to the pipeline by default.
```powershell $config = @' name: example enabled: true ports: [80, 443] '@ | ConvertFrom-Yaml
$config.name $config.ports[0] ```
Pipeline strings are joined with LF and parsed as one stream. This makes line-oriented input work as expected:
powershell
$config = Get-Content -Path '.\config.yaml' | ConvertFrom-Yaml
Use -NoEnumerate when a top-level sequence must remain one pipeline record:
powershell
$servers = @'
- name: web-1
- name: web-2
'@ | ConvertFrom-Yaml -NoEnumerate
Every YAML document is returned separately:
```powershell $documents = @(@'
name: first¶
name: second '@ | ConvertFrom-Yaml) ```
Use -AsHashtable for insertion-ordered dictionaries and mappings with complex,
non-string, empty, or case-colliding keys:
powershell
$mapping = @'
? [region, port]
: eu-1
'@ | ConvertFrom-Yaml -AsHashtable
The YAML to PowerShell object model¶
This section describes exactly what a parsed document becomes, so you can predict the shape before you run anything.
Scalars¶
Plain, unquoted scalars are resolved with the YAML 1.2 core schema. Quoted and block scalars are always strings unless you give them an explicit tag.
| YAML text | PowerShell type | Value |
|---|---|---|
null, Null, NULL, ~, empty |
none | $null |
true, True, TRUE, false, False, FALSE |
System.Boolean |
$true / $false |
7, -7, +7, 010 |
System.Int32 |
7, -7, 7, 10 |
0o17, 0x1F |
System.Int32 |
15, 31 |
9223372036854775807 |
System.Int64 |
widened past Int32 |
9223372036854775808 |
System.Numerics.BigInteger |
widened past Int64 |
1.5, .5, 1., 3.0 |
System.Decimal |
exact, no exponent |
1e3, 0.5e3, 2.3e-5 |
System.Double |
exponent present |
.inf, +.inf, -.inf, .nan |
System.Double |
infinity and NaN |
| anything else | System.String |
the scalar text |
Integers pick the narrowest of Int32, Int64 and BigInteger that holds the
value. Finite decimal forms without an exponent become Decimal when
representable, so 0.1 keeps its exact value; anything with an exponent becomes
Double. 010 is decimal ten, because the core schema has no leading-zero octal.
Forms that only exist in YAML 1.1 are not resolved and stay strings:
powershell
$v = 'a: yes', 'b: 0b1010', 'c: 1_000', 'd: 2001-12-14' -join "`n" | ConvertFrom-Yaml
$v.a.GetType().Name # String
$v.b.GetType().Name # String
$v.c.GetType().Name # String
$v.d.GetType().Name # String - an implicit timestamp is text
Explicitly tagged scalars¶
| Tag | PowerShell type |
|---|---|
!!str |
System.String |
!!null |
$null |
!!bool |
System.Boolean |
!!int |
System.Int32, System.Int64, or System.Numerics.BigInteger |
!!float |
System.Decimal or System.Double |
!!binary |
System.Byte[] decoded from base64 |
!!timestamp |
System.DateTime or System.DateTimeOffset |
A standard tag whose text does not fit the tag fails rather than falling back to a
string. !!bool "yes" raises YamlInvalidTaggedScalar, because yes is not a
YAML 1.2 Boolean. Unknown application tags are discarded safely: !custom 5
becomes the string 5, and a tagged collection keeps its sequence or mapping
shape.
Timestamps are only constructed for the explicit !!timestamp tag:
```powershell $t = @' day: !!timestamp 2001-12-14 zoned: !!timestamp 2001-12-14T21:59:43.10-05:00 naive: !!timestamp "2001-12-14 21:59:43" '@ | ConvertFrom-Yaml
$t.day.GetType().Name # DateTime, Kind Utc, midnight $t.zoned.GetType().Name # DateTimeOffset, offset preserved $t.naive.GetType().Name # DateTime, Kind Utc ```
A value with a zone (Z or ±hh:mm) becomes DateTimeOffset. A date-only or
zone-less value becomes DateTime with Kind set to Utc.
Mappings¶
By default a mapping becomes a PSCustomObject whose note-properties are added in
source order, so Format-List and PSObject.Properties both report the original
key order.
With -AsHashtable a mapping becomes a
System.Collections.Specialized.OrderedDictionary instead. The switch is
recursive: nested mappings are dictionaries too. Dictionary keys are the projected
keys themselves, so keys that are not usable as property names survive:
```powershell $d = @' ? [region, port] : eu-1 ~: nullkey 1: number '1': text '@ | ConvertFrom-Yaml -AsHashtable
$d.Count # 4 ($d.Keys | Select-Object -First 1).GetType() # System.Object[] - the complex key ```
The four key types are Object[], DBNull, Int32 and String, in source order.
A YAML null key is stored as [System.DBNull]::Value, because a dictionary cannot
hold a $null key. The integer key 1 and the string key '1' are distinct
entries.
Sequences¶
A sequence becomes System.Object[]. Nested sequences are always arrays, whatever
the enumeration options.
A top-level sequence is enumerated onto the pipeline by default, one record per
item. -NoEnumerate writes the whole sequence as a single record:
powershell
(ConvertFrom-Yaml -Yaml "- 1`n- 2" | Measure-Object).Count # 2
(ConvertFrom-Yaml -Yaml "- 1`n- 2" -NoEnumerate | Measure-Object).Count # 1
An empty top-level sequence emits nothing by default, and one empty array with
-NoEnumerate.
Multi-document streams¶
One object is emitted per document, in document order. An empty document emits
$null:
powershell
@(ConvertFrom-Yaml -Yaml "---`n---`n").Count # 2, both $null
-NoEnumerate applies per document, so a stream of two sequence documents writes
two records instead of one per item.
Anchors and aliases¶
An alias to a collection projects to the same object instance, in both
PSCustomObject and -AsHashtable mode. Editing through one reference is visible
through the other:
```powershell $g = @' defaults: &d { region: eu-1 } primary: *d '@ | ConvertFrom-Yaml
[object]::ReferenceEquals($g.defaults, $g.primary) # True ```
Recursive aliases are supported, so a node can contain itself. Aliases to scalars carry the value, not an identity: scalars are compared by value.
Collection tags¶
| Tag | PowerShell projection |
|---|---|
!!seq |
System.Object[] |
!!map |
PSCustomObject, or OrderedDictionary with -AsHashtable |
!!set |
OrderedDictionary with $null values, always, even without -AsHashtable |
!!omap |
OrderedDictionary, duplicate keys rejected |
!!pairs |
System.Object[] of single-entry OrderedDictionary values, duplicates allowed |
``powershell
$s = ConvertFrom-Yaml -Yaml "!!setn? a`n? b"
$s.GetType().Name # OrderedDictionary
$null -eq $s['a'] # True
$p = ConvertFrom-Yaml -Yaml "!!pairsn- a: 1n- a: 2" -NoEnumerate
$p.Count # 2, both keyed 'a'
```
!!set, !!omap and !!pairs need dictionary semantics to keep their meaning, so
they select dictionary projection for themselves regardless of -AsHashtable.
What fails instead of losing data¶
Default PSCustomObject projection only accepts mapping keys that can become
PowerShell properties without loss. Everything else is a terminating,
specifically classified error rather than a silent rename or drop.
| Error ID | Cause | Fix |
|---|---|---|
YamlMappingKeyNotString |
The key is a sequence, mapping, number, Boolean, null, or empty string. | Use -AsHashtable. |
YamlPropertyNameCollision |
Two keys differ only by case, such as Name and name. |
Use -AsHashtable. |
YamlPropertyNameReserved |
The key is PSObject, PSTypeNames, PSBase, PSAdapted, or PSExtended. |
Use -AsHashtable. |
YamlDuplicateKey |
The same key appears twice in one mapping. | Fix the document. Rejected in both modes. |
YamlDuplicateKey is a representation-level rule and applies with -AsHashtable
too, including structurally equal complex keys. The other three are property-model
restrictions that -AsHashtable lifts.
Worked example¶
yaml
name: example
retries: 3
ratio: 0.25
enabled: true
notes: null
created: !!timestamp 2024-05-01T09:30:00Z
tags: [alpha, beta]
defaults: &defaults
region: eu-1
tier: standard
services:
- name: api
settings: *defaults
- name: worker
settings: *defaults
Parsing that document with ConvertFrom-Yaml produces one PSCustomObject:
text
PSCustomObject
├─ name System.String 'example'
├─ retries System.Int32 3
├─ ratio System.Decimal 0.25
├─ enabled System.Boolean True
├─ notes $null
├─ created System.DateTimeOffset 2024-05-01T09:30:00+00:00
├─ tags System.Object[]
│ ├─ [0] System.String 'alpha'
│ └─ [1] System.String 'beta'
├─ defaults PSCustomObject ◄─────────┐ same instance
│ ├─ region System.String 'eu-1' │
│ └─ tier System.String 'standard'│
└─ services System.Object[] │
├─ [0] PSCustomObject │
│ ├─ name System.String 'api' │
│ └─ settings PSCustomObject ───────────────┤
└─ [1] PSCustomObject │
├─ name System.String 'worker' │
└─ settings PSCustomObject ───────────────┘
```powershell $doc = Get-Content -Path '.\config.yaml' -Raw | ConvertFrom-Yaml
$doc.retries.GetType().Name # Int32 $doc.ratio.GetType().Name # Decimal $doc.created.GetType().Name # DateTimeOffset $doc.services[1].name # worker
[object]::ReferenceEquals($doc.defaults, $doc.services[0].settings) # True ```
Serialize PowerShell values¶
ConvertTo-Yaml supports PSCustomObject and explicit PSObject note-property
bags, dictionaries, sequences, strings, characters, Booleans, integer and
floating-point numbers, BigInteger, DateTime, DateTimeOffset, enums, null,
and byte arrays.
```powershell $yaml = [ordered]@{ name = 'example' enabled = $true ports = @(80, 443) } | ConvertTo-Yaml -ExplicitDocumentStart
$roundTrip = $yaml | ConvertFrom-Yaml ```
Multiple pipeline records are collected into one top-level YAML sequence:
powershell
'one', 'two' | ConvertTo-Yaml
Pass an array directly when it represents one input value:
powershell
$items = @('one', 'two')
ConvertTo-Yaml -InputObject $items
-EnumsAsStrings emits enum names instead of their underlying numeric values.
-Indent accepts 2 through 9 spaces. -Depth, -MaxNodes, and
-MaxScalarLength constrain serialization. The maximum supported depth is 128,
and the default is 100.
Repeated acyclic collection references are emitted with anchors and aliases.
Cyclic graphs and unsupported runtime objects fail specifically; values are never
silently truncated or converted with ToString().
Round-trip expectations¶
A PowerShell object does not retain YAML presentation, so a data round trip does not preserve comments, scalar style, tag spelling or handles, anchor names, mapping presentation, line endings, or source formatting. Unknown application tags are not reconstructed.
Exact integer CLR widths and enum CLR types are not reconstructed after a YAML
round trip. Finite non-exponent decimal values are constructed as Decimal when
representable; other finite floats use Double. The emitter writes a deliberately
limited YAML 1.2-compatible subset.
When presentation matters more than the values, format or merge the YAML directly with the Streams commands, which never project through PowerShell values at all.