Skip to content

Latest commit

 

History

History
211 lines (165 loc) · 7.26 KB

File metadata and controls

211 lines (165 loc) · 7.26 KB

Yaml

Yaml converts between YAML streams and PowerShell values. Its parser, schema construction, graph projection, and emitter are implemented in the module's PowerShell source, with no external parser library or runtime assembly dependency.

Compatibility target: PowerShell Core LTS and newer, with the source and artifact contract pinned to PowerShell 7.6 (CompatiblePSEditions = 'Core').

The implementation is layered: Unicode and c-printable validation feeds a document/directive scanner; context-aware block and flow readers produce syntax tokens; an iterative composer builds the representation graph; the core-schema constructor and PowerShell projector create public values. Serialization uses a separate safe value classifier, iterative reference-graph normalizer, and presentation emitter. Internal arrays, nulls, and byte arrays move through boxed values rather than the PowerShell pipeline.

Installation

Install-PSResource -Name Yaml
Import-Module -Name Yaml

The module exports:

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.
Test-Yaml Test YAML syntax, tags, duplicate keys, and configured resource limits.

Parse YAML

Ordinary string-key mappings become ordered PSCustomObject values. A top-level sequence writes its items to the pipeline by default.

$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:

$config = Get-Content -Path '.\config.yaml' | ConvertFrom-Yaml

Use -NoEnumerate when a top-level sequence must remain one pipeline record:

$servers = @'
- name: web-1
- name: web-2
'@ | ConvertFrom-Yaml -NoEnumerate

Every YAML document is returned separately:

$documents = @(@'
---
name: first
---
name: second
'@ | ConvertFrom-Yaml)

Use -AsHashtable for insertion-ordered dictionaries and mappings with complex, non-string, empty, or case-colliding keys:

$mapping = @'
? [region, port]
: eu-1
'@ | ConvertFrom-Yaml -AsHashtable

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.

$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:

'one', 'two' | ConvertTo-Yaml

Pass an array directly when it represents one input value:

$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().

Validate YAML

if (Get-Content -Path '.\config.yaml' | Test-Yaml) {
    'The YAML stream is valid.'
}

Test-Yaml uses the same parser and limits as ConvertFrom-Yaml. It returns $false for YAML-specific failures, including duplicate keys and resource limit violations. Unexpected runtime failures are not suppressed.

Data model and safety

  • Plain implicit scalars use the YAML 1.2 core schema. YAML 1.1-only Boolean words such as yes and untagged timestamps remain strings.
  • Standard explicit tags are handled without reflection: str, null, bool, int, float, binary, timestamp, seq, map, set, omap, and pairs.
  • Unknown application tags are discarded safely. Tagged scalars become strings and tagged collections retain their sequence or mapping shape.
  • Duplicate mapping keys are rejected after scalar construction, including structurally equal complex keys.
  • Aliases preserve collection reference identity where PowerShell can represent it.
  • YAML merge keys are not expanded; << is ordinary mapping data under the YAML 1.2 core schema.
  • Parsing defaults to depth 100, 100000 nodes, 1000 aliases, 1048576 decoded characters per scalar, 1024 characters per expanded tag, 65536 cumulative expanded tag characters, and 4096 digits per numeric scalar. The corresponding limit parameters can be lowered for untrusted input.
  • The public maximum depth of 128 is exercised for parsing and serialization in fresh PowerShell 7.6+ artifact tests.

Default object projection requires mapping keys that can be represented without loss as PowerShell properties. Use -AsHashtable when that restriction does not fit the data.

Conformance corpus

The offline test gate runs the latest official yaml-test-suite source release, v2022-01-17, at commit 45db50aecf9b1520f8258938c88f396e96f30831. Its data-2022-01-17 export is pinned at commit 6e6c296ae9c9d2d5c4134b4b64d01b29ac19ff6f with archive SHA-256 47C173AFFEB480517B30FB77DC8C76FD48609B9B65DD1C1D3D0D0BAEE48D6AA9. The archive contains 402 inputs:

Surface Pass PolicyDifference Fail NotApplicable
Syntax and composition 400 2 0 0
Representation events 308 0 0 94
JSON projection 277 2 0 123
out.yaml projection 241 0 0 161
Emit and round trip 306 0 0 96

All 94 fixtures marked invalid are rejected. The valid 2JQS and X38W inputs are syntactically recognized and produce matching representation events, then are rejected during load validation because representation mapping keys must be unique. They are not unsupported grammar.

The two JSON projection differences are 565N, where !!binary intentionally becomes byte[] instead of a Base64 string, and J7PZ, where legacy !!omap intentionally becomes System.Collections.Specialized.OrderedDictionary instead of remaining a sequence of one-entry mappings. No event mismatch is classified as policy. The deterministic runner reports no unexplained failures.

These results are a pinned compatibility measurement, not a claim that a finite corpus proves complete YAML 1.2.2 compliance.

Compatibility boundaries

The module preserves data values and graph sharing where representable. A PowerShell object does not retain YAML presentation details, so data round trips do 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.

Contributing

See CONTRIBUTING.md.