Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/PSModule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ Test:
# Skip: true
# MacOS:
# Skip: true
# Build:
# Docs:
# Skip: true
Build:
Docs:
# PlatyPS loads a conflicting YamlDotNet assembly before importing this module.
Skip: true

Linter:
env:
Expand Down
6 changes: 6 additions & 0 deletions .github/linters/.yaml-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
extends: relaxed

# Official conformance fixtures intentionally preserve invalid and unusual YAML.
ignore: |
**/tests/fixtures/**
7 changes: 6 additions & 1 deletion .github/workflows/Process-PSModule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ permissions:

jobs:
Process-PSModule:
uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@11117919e65242d3388727819a751f74ad24ea9e # v5.5.0
uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 # v6.1.4
with:
ImportantFilePatterns: |
^src/
^tests/
^README\.md$
secrets: inherit
175 changes: 138 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,170 @@
# {{ NAME }}
# Yaml

{{ DESCRIPTION }}
`Yaml` converts between YAML streams and PowerShell values. It uses
YamlDotNet's low-level parser and emitter while applying YAML 1.2 core-schema
rules in PowerShell, without activating .NET types from YAML tags.

## Prerequisites
## Installation

```powershell
Install-PSResource -Name Yaml
Import-Module -Name Yaml
```

This uses the following external resources:
- The [PSModule framework](https://github.com/PSModule) for building, testing and publishing the module.
The module exports:

## Installation
| 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

To install the module from the PowerShell Gallery, you can use the following command:
Ordinary string-key mappings become ordered `PSCustomObject` values. A
top-level sequence writes its items to the pipeline by default.

```powershell
Install-PSResource -Name {{ NAME }}
Import-Module -Name {{ NAME }}
$config = @'
name: example
enabled: true
ports: [80, 443]
'@ | ConvertFrom-Yaml

$config.name
$config.ports[0]
```

## Usage
Pipeline strings are joined with LF and parsed as one stream. This makes
line-oriented input work as expected:

Here is a list of example that are typical use cases for the module.

### Example 1: Greet an entity
```powershell
$config = Get-Content -Path '.\config.yaml' | ConvertFrom-Yaml
```

Provide examples for typical commands that a user would like to do with the module.
Use `-NoEnumerate` when a top-level sequence must remain one pipeline record:

```powershell
Greet-Entity -Name 'World'
Hello, World!
$servers = @'
- name: web-1
- name: web-2
'@ | ConvertFrom-Yaml -NoEnumerate
```

### Example 2
Every YAML document is returned separately:

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

Provide examples for typical commands that a user would like to do with the module.
Use `-AsHashtable` for insertion-ordered dictionaries and mappings with
complex, non-string, empty, or case-colliding keys:

```powershell
Import-Module -Name PSModuleTemplate
$mapping = @'
? [region, port]
: eu-1
'@ | ConvertFrom-Yaml -AsHashtable
```

### Find more examples
## Serialize PowerShell values

To find more examples of how to use the module, please refer to the [examples](examples) folder.
`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.

Alternatively, you can use the Get-Command -Module 'This module' to find more commands that are available in the module.
To find examples of each of the commands you can use Get-Help -Examples 'CommandName'.
```powershell
$yaml = [ordered]@{
name = 'example'
enabled = $true
ports = @(80, 443)
} | ConvertTo-Yaml -ExplicitDocumentStart

## Documentation
$roundTrip = $yaml | ConvertFrom-Yaml
```

Link to further documentation if available, or describe where in the repository users can find more detailed documentation about
the module's functions and features.
Multiple pipeline records are collected into one top-level YAML sequence:

## Contributing
```powershell
'one', 'two' | ConvertTo-Yaml
```

Pass an array directly when it represents one input value:

Coder or not, you can contribute to the project! We welcome all contributions.
```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.

### For Users
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()`.

If you don't code, you still sit on valuable information that can make this project even better. If you experience that the
product does unexpected things, throw errors or is missing functionality, you can help by submitting bugs and feature requests.
Please see the issues tab on this project and submit a new issue that matches your needs.
## Validate YAML

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

If you do code, we'd love to have your contributions. Please read the [Contribution guidelines](CONTRIBUTING.md) for more information.
You can either help by picking up an existing issue or submit a new one if you have an idea for a new feature or improvement.
`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, and 1048576
decoded characters per scalar. The corresponding limit parameters can be
lowered for untrusted input.

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.

## 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 numeric CLR widths and enum CLR types are also not reconstructed after a
YAML round trip. The emitter writes a deliberately limited YAML
1.2-compatible subset even though YamlDotNet describes its emitter as YAML
1.1.

## Third-party component

The packaged module includes YamlDotNet 18.1.0
`lib/netstandard2.0/YamlDotNet.dll`. See
[`src/THIRD-PARTY-NOTICES.txt`](src/THIRD-PARTY-NOTICES.txt) and
[`src/licenses/YamlDotNet.LICENSE.txt`](src/licenses/YamlDotNet.LICENSE.txt).

## Acknowledgements
## Contributing

Here is a list of people and projects that helped this project in some way.
See [CONTRIBUTING.md](CONTRIBUTING.md).
52 changes: 39 additions & 13 deletions examples/General.ps1
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
<#
.SYNOPSIS
This is a general example of how to use the module.
<#
.SYNOPSIS
Demonstrates the public Yaml commands.
#>

# Import the module
Import-Module -Name 'PSModule'
Import-Module -Name Yaml

# Define the path to the font file
$FontFilePath = 'C:\Fonts\CodeNewRoman\CodeNewRomanNerdFontPropo-Regular.tff'
$yaml = @'
---
name: example
enabled: true
ports: [80, 443]
'@

# Install the font
Install-Font -Path $FontFilePath -Verbose
# Parse a mapping to an ordered PSCustomObject.
$config = $yaml | ConvertFrom-Yaml
$config

# List installed fonts
Get-Font -Name 'CodeNewRomanNerdFontPropo-Regular'
# Keep a top-level sequence as one pipeline record.
$servers = @'
- name: web-1
- name: web-2
'@ | ConvertFrom-Yaml -NoEnumerate
$servers.Count

# Uninstall the font
Get-Font -Name 'CodeNewRomanNerdFontPropo-Regular' | Uninstall-Font -Verbose
# Preserve mappings whose keys cannot be PowerShell property names.
$complexMapping = @'
? [region, port]
: eu-1
'@ | ConvertFrom-Yaml -AsHashtable
$complexMapping

# Serialize supported PowerShell data and parse it again.
$outputYaml = [ordered]@{
name = 'example'
enabled = $true
ports = @(80, 443)
} | ConvertTo-Yaml -ExplicitDocumentStart

$outputYaml
$outputYaml | ConvertFrom-Yaml

# Test syntax, duplicate keys, tags, and resource limits without conversion.
$isValid = $outputYaml | Test-Yaml
$isValid
18 changes: 18 additions & 0 deletions src/THIRD-PARTY-NOTICES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
YamlDotNet 18.1.0
=================

Copyright (c) Antoine Aubry and contributors.
Licensed under the MIT License. See licenses/YamlDotNet.LICENSE.txt.

Package:
https://api.nuget.org/v3-flatcontainer/yamldotnet/18.1.0/yamldotnet.18.1.0.nupkg
Package repository:
https://github.com/aaubry/YamlDotNet/tree/748334a8fa7c227740018b284b71ad95cc6b7fc7
Bundled asset:
lib/netstandard2.0/YamlDotNet.dll
Target framework:
netstandard2.0
Package SHA-256:
59FFE65ADE67AD9D886267F877B634A450363CB81B94E19DB9CA4C36461416F6
Bundled DLL SHA-256:
91CD6D1FD0AE5B64BF6B252EB3B1AF2382CBC599C29045427C45FE8403D4295D
Binary file added src/assemblies/YamlDotNet.dll
Binary file not shown.
20 changes: 20 additions & 0 deletions src/functions/private/Confirm-YamlScalarLength.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function Confirm-YamlScalarLength {
<#
.SYNOPSIS
Enforces the configured length limit for an emission scalar.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[pscustomobject] $Node,

[Parameter(Mandatory)]
[pscustomobject] $State
)

if ($Node.Value.Length -gt $State.MaxScalarLength) {
throw (New-YamlSerializationException -ErrorId 'YamlScalarLimitExceeded' -Message (
"A scalar exceeds the configured limit of $($State.MaxScalarLength) characters."
))
}
}
52 changes: 52 additions & 0 deletions src/functions/private/ConvertFrom-YamlInteger.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
function ConvertFrom-YamlInteger {
<#
.SYNOPSIS
Constructs the narrowest supported numeric value from a YAML integer.
#>
[CmdletBinding()]
[OutputType([int], [long], [System.Numerics.BigInteger])]
param (
[Parameter(Mandatory)]
[string] $Value
)

$sign = [System.Numerics.BigInteger]::One
$digits = $Value
$base = 10

if ($digits.StartsWith('+', [System.StringComparison]::Ordinal)) {
$digits = $digits.Substring(1)
} elseif ($digits.StartsWith('-', [System.StringComparison]::Ordinal)) {
$sign = [System.Numerics.BigInteger]::MinusOne
$digits = $digits.Substring(1)
}

if ($digits.StartsWith('0o', [System.StringComparison]::Ordinal)) {
$base = 8
$digits = $digits.Substring(2)
} elseif ($digits.StartsWith('0x', [System.StringComparison]::Ordinal)) {
$base = 16
$digits = $digits.Substring(2)
}

$result = [System.Numerics.BigInteger]::Zero
foreach ($character in $digits.ToCharArray()) {
if ($character -ge [char] '0' -and $character -le [char] '9') {
$digit = [int] $character - [int] [char] '0'
} else {
$digit = 10 + ([int] [char]::ToUpperInvariant($character) - [int] [char] 'A')
}
$result = ($result * $base) + $digit
}
$result *= $sign

if ($result -ge [int]::MinValue -and $result -le [int]::MaxValue) {
Write-Output -InputObject ([int] $result) -NoEnumerate
return
}
if ($result -ge [long]::MinValue -and $result -le [long]::MaxValue) {
Write-Output -InputObject ([long] $result) -NoEnumerate
return
}
Write-Output -InputObject $result -NoEnumerate
}
Loading
Loading