🚀 [Feature]: YAML conversion now possible with ConvertFrom-Yaml and ConvertTo-Yaml#19
🚀 [Feature]: YAML conversion now possible with ConvertFrom-Yaml and ConvertTo-Yaml#19Marius Storhaug (MariusStorhaug) wants to merge 22 commits intomainfrom
Conversation
Super-linter summary
Super-linter detected linting errors For more information, see the GitHub Actions workflow run Powered by Super-linter MARKDOWNNATURAL_LANGUAGEPOWERSHELL |
Super-linter summary
Super-linter detected linting errors For more information, see the GitHub Actions workflow run Powered by Super-linter MARKDOWNNATURAL_LANGUAGEPOWERSHELLSPELL_CODESPELL |
Super-linter summary
Super-linter detected linting errors For more information, see the GitHub Actions workflow run Powered by Super-linter POWERSHELLSPELL_CODESPELL |
Super-linter summary
Super-linter detected linting errors For more information, see the GitHub Actions workflow run Powered by Super-linter POWERSHELLSPELL_CODESPELL |
… escape handler, suppress false-positive ShouldProcess on Remove-YamlInlineComment, rename Get-YamlMappingPairs to singular noun, remove unused Options param from Format-YamlKey, use here-string in test to avoid codespell nd false positive
Super-linter summary
Super-linter detected linting errors For more information, see the GitHub Actions workflow run Powered by Super-linter POWERSHELL |
…arations - Add [PSProvideCommentHelp] comment blocks to 8 private helper functions: ConvertFrom-YamlMapping, ConvertFrom-YamlSequence, Test-YamlMappingType, Test-YamlSequenceType, ConvertTo-YamlMapping, ConvertTo-YamlSequence, Format-YamlDoubleQuoted, Format-YamlKey - Add [OutputType] to ConvertFrom-YamlMapping, ConvertFrom-YamlSequence, ConvertFrom-YamlScalar - Suppress PSUseOutputTypeCorrectly on 4 functions that use comma-unary operator to prevent collection unwrapping (ConvertFrom-YamlLineStream, ConvertFrom-YamlSequence, Get-YamlMappingPair, ConvertFrom-Yaml)
…escription, fix 'Id' to 'ID' in ConvertTo-Yaml example
Super-linter summary
All files and directories linted successfully For more information, see the GitHub Actions workflow run Powered by Super-linter |
There was a problem hiding this comment.
Pull request overview
This PR adds PowerShell-native YAML serialization/deserialization cmdlets (ConvertFrom-Yaml / ConvertTo-Yaml) plus Yml aliases, implemented in pure PowerShell with a focused YAML 1.2.2 core-schema subset and backed by new Pester coverage. It also removes the prior placeholder cmdlet/test and updates docs/examples to reflect the new module purpose.
Changes:
- Added
ConvertFrom-Yamlparser (line-stream tokenizer + recursive-descent node parser) with-AsHashtable,-NoEnumerate, and-Depth. - Added
ConvertTo-Yamlemitter (recursive node writer) with-Depth,-Indent,-EnumsAsStrings, and-AsArray. - Replaced placeholder cmdlet/tests with YAML-focused tests, and updated README + examples.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/PSModuleTest.Tests.ps1 | Removes placeholder Pester test. |
| tests/ConvertTo-Yaml.Tests.ps1 | Adds Pester coverage for YAML emission + round-trip scenarios. |
| tests/ConvertFrom-Yaml.Tests.ps1 | Adds Pester coverage for YAML parsing, options, comments/markers, and core-schema scalar rules. |
| src/functions/public/Get-PSModuleTest.ps1 | Removes placeholder public function. |
| src/functions/public/ConvertTo-Yaml.ps1 | New public YAML serializer cmdlet + alias. |
| src/functions/public/ConvertFrom-Yaml.ps1 | New public YAML parser cmdlet + alias. |
| src/functions/private/ConvertTo-YamlNode.ps1 | Adds recursive emitter + scalar formatting/quoting helpers. |
| src/functions/private/ConvertFrom-YamlNode.ps1 | Adds recursive-descent parser for mappings/sequences/scalars. |
| src/functions/private/ConvertFrom-YamlLineStream.ps1 | Adds tokenizer that strips comments/blank lines/markers and tracks indentation. |
| examples/General.ps1 | Updates example usage to demonstrate YAML cmdlets. |
| README.md | Replaces template placeholders with YAML module documentation and examples. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The PSModule SourceCode test suite enforces that each .ps1 file contains exactly one function whose name matches the filename. The three private helper files each contained multiple helper functions, causing FunctionCount and FunctionName test failures on all platforms. Split into 16 individual files: - ConvertFrom-YamlLineStream.ps1: extracted Remove-YamlInlineComment - ConvertFrom-YamlNode.ps1: extracted ConvertFrom-YamlMapping, ConvertFrom-YamlSequence, Find-YamlMappingColon, ConvertFrom-YamlScalar, Expand-YamlDoubleQuoted - ConvertTo-YamlNode.ps1: extracted Test-YamlMappingType, Test-YamlSequenceType, ConvertTo-YamlMapping, ConvertTo-YamlSequence, Get-YamlMappingPair, Format-YamlScalar, Format-YamlString, Format-YamlDoubleQuoted, Format-YamlKey, Test-YamlPlainSafe All 56 Pester tests pass locally.
Super-linter summary
All files and directories linted successfully For more information, see the GitHub Actions workflow run Powered by Super-linter |
…se [pscustomobject] cast to handle member name collisions - Keys like true/false/null/~ are now preserved as literal strings instead of being type-resolved then cast back (which changed true->True, null->empty string) - Quoted keys still have their escapes expanded correctly - Replace Add-Member loop with [pscustomobject]$map cast to avoid errors when keys collide with built-in member names like ToString/GetType - Add 3 tests: YAML-special keys, quoted key escapes, member name collision keys
Super-linter summary
All files and directories linted successfully For more information, see the GitHub Actions workflow run Powered by Super-linter |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…} round-tripping
- Fix if/else expressions that collapsed empty arrays to $null (PowerShell
unrolls @() to nothing in expression output). Converted to statement form
in ConvertTo-YamlMapping, ConvertTo-YamlSequence, ConvertTo-YamlNode,
Get-YamlMappingPair, and ConvertTo-Yaml.
- Add indentation to empty {} and [] output in ConvertTo-YamlMapping and
ConvertTo-YamlSequence so nested empty collections serialize correctly.
- Add indentation to depth-exceeded branch in ConvertTo-YamlNode.
- Add inline empty-collection checks in ConvertTo-YamlSequence for nested
mapping/sequence values within sequence-item-mappings.
- Recognize {} and [] inline literals in ConvertFrom-YamlMapping and
ConvertFrom-YamlSequence to restore round-trip fidelity.
- Add 17 new tests: empty collection rendering, depth-exceeded indentation,
{} / [] parsing, and round-trip preservation of empty collections.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… in nested-sequence depth test
Super-linter summary
All files and directories linted successfully For more information, see the GitHub Actions workflow run Powered by Super-linter |
…dent, test array IndexOf Thread r3179984727 (ConvertFrom-YamlSequence): inline mapping child indent was hardcoded to Indent+2, breaking continuation lines when extra spaces follow the dash (e.g. '- a: 1'). Fixed by computing extraSpaces from leading spaces in afterDash and setting childIndent = Indent + 2 + extraSpaces and stripping those spaces from the synthetic Content. Added test 'Parses sequence inline mappings with extra spaces after the dash'. Thread r3179984765 (ConvertFrom-YamlScalar): [double]::TryParse with NumberStyles.Float accepts NaN, Infinity, -Infinity etc., which are .NET-specific and not part of the YAML 1.2.2 core schema (the spec uses .inf/.nan dot-prefix forms). Added a pre-guard regex -imatch '^[+-]?(infinity|nan)$' to return these as plain strings before TryParse. Added test 'Treats .NET-specific special float tokens as strings (YAML 1.2.2)'. Thread r3179984792 (ConvertTo-Yaml.Tests.ps1): string arrays from -split have no instance IndexOf() method; the call would throw at runtime. Replaced \.IndexOf(\) with [array]::IndexOf(\, \).
Super-linter summary
All files and directories linted successfully For more information, see the GitHub Actions workflow run Powered by Super-linter |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- ConvertFrom-YamlMapping: support indentless sequences (e.g. key:\n- a\n- b) where the sequence starts at the same indent as the parent mapping key. Removes misleading comment that claimed strict indentation was required. - ConvertFrom-Yaml: add post-parse check that all preprocessed lines were consumed. Throws a terminating error with the first unconsumed line number and content when trailing content is detected. - Tests: 4 new tests — indentless sequences (3 variants), unconsumed content. All 129 tests pass.
Super-linter summary
Super-linter detected linting errors For more information, see the GitHub Actions workflow run Powered by Super-linter POWERSHELL |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…nd README escape lists
Super-linter summary
All files and directories linted successfully For more information, see the GitHub Actions workflow run Powered by Super-linter |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…n, strengthen empty-array and unsigned-enum test assertions
Super-linter summary
All files and directories linted successfully For more information, see the GitHub Actions workflow run Powered by Super-linter |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ent for plain scalars Quote characters inside plain (unquoted) scalars were incorrectly toggling quote state, causing colon detection and inline comment stripping to fail for values like owner's: Bob or name: O'Connor # comment. Added inPlain flag to both functions so quote characters only open quoted regions at token boundaries (position 0, after ': ', after '- '), not mid-token in plain scalars. Added 5 tests covering apostrophe/double-quote in plain keys, values, and sequence items with inline comments.
Super-linter summary
Super-linter detected linting errors For more information, see the GitHub Actions workflow run Powered by Super-linter POWERSHELLSPELL_CODESPELL |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| string. Mirrors the parameter shape of `ConvertTo-Json` where applicable. | ||
|
|
||
| Out of scope for this version: flow style (`[a, b]`, `{a: 1}`), block scalars | ||
| (`|`, `>`), anchors/aliases, tags, and timestamp formatting. |
| # Float. | ||
| # Reject .NET-specific special float tokens that are not part of the YAML 1.2.2 core schema. | ||
| # Core schema uses .inf/.Inf/.INF/.nan/.NaN/.NAN (dot-prefix form). The bare NaN/Infinity | ||
| # words are accepted by [double]::TryParse but must remain plain strings per the spec. | ||
| if ($value -imatch '^[+-]?(infinity|nan)$') { return $value } | ||
| $dblVal = 0.0 | ||
| if ([double]::TryParse($value, [System.Globalization.NumberStyles]::Float, [cultureinfo]::InvariantCulture, [ref] $dblVal)) { | ||
| return $dblVal | ||
| } |
YAML serialization now ships in PowerShell-native form.
ConvertFrom-Yamlturns a valid YAML string into a[PSCustomObject](or an ordered hashtable with-AsHashtable), andConvertTo-Yamlserializes objects, hashtables, and arrays back into a YAML string. Both functions follow the parameter shape ofConvertFrom-Json/ConvertTo-Jsonand are also available under theYmlnoun aliases (ConvertFrom-Yml/ConvertTo-Yml).Scalar resolution aligns with YAML 1.2.2 (October 2021) — the latest revision of the spec — using its core schema. Only the lowercase canonical literals are recognised:
true/falseare booleans,null/~/ empty are null, and everything else is a string unless it parses as a number.New: ConvertFrom-Yaml
Parses a valid YAML string into a PowerShell object. Returns a
[PSCustomObject]by default or an[ordered]hashtable with-AsHashtable. Use-NoEnumerateto keep a single-element top-level sequence as an array, and-Depth(default1024) as a recursion safety net.The input must be a valid YAML string. The cmdlet does not read files or extract markdown frontmatter — that is the caller's responsibility (a separate Markdown module is the right place to extract frontmatter; the result can then be piped into
ConvertFrom-Yaml). The YAML document-start (---) and document-end (...) markers are tolerated.The supported YAML subset covers block-style mappings, block-style sequences, nested structures, single- and double-quoted strings (with
\n,\t,\r,\\,\"escapes in double quotes), integers, doubles, lowercase booleans (true/false) and null (null/~/ empty value). Full-line and inline#comments are ignored. YAML 1.1 boolean aliases (yes,no,on,off) and non-canonical case variants (True,TRUE,Null,NULL) are treated as plain strings, matching the YAML 1.2.2 core schema.New: ConvertTo-Yaml
Serializes objects, hashtables/dictionaries, and arrays into a YAML block-style string. Mirrors
ConvertTo-Jsonparameters where applicable.-Depth1024.ToString().-Indent2-EnumsAsStrings-AsArrayStrings that match a YAML 1.2.2 core schema literal (
true,false,null,~) or that would otherwise parse as a number are quoted automatically — round-tripping$obj | ConvertTo-Yaml | ConvertFrom-Yamlreturns equivalent values and types.Aliases
Both functions register a noun alias for shorter invocation:
What's not in this version
The following are intentionally out of scope and remain as separate issues / outside the module's responsibility:
[a, b],{a: 1}) — see 🚀[Feature]: Support inline arrays #7|,>) for multiline strings — see 🚀[Feature]: Support multiline strings #8Test-Yamlvalidator — see 🚀[Feature]: Function: Test-Yaml #4Format-Yamlformatter — see 🚀[Feature]: Function: Format-Yaml #5!!timestampparsingConvertFrom-Yaml-CompressfromConvertTo-Jsonis intentionally omitted because the YAML analog is flow style, which is deferred.Technical Details
-ceq) so non-canonical variants (True,Yes,NULL, etc.) remain strings.src/functions/public/ConvertFrom-Yaml.ps1,src/functions/public/ConvertTo-Yaml.ps1. Aliases registered via[Alias()]attribute.src/functions/private/:ConvertFrom-YamlLineStream.ps1— tokenizes input into indent-aware lines, skips comments, blank lines, and---/...document markers.ConvertFrom-YamlNode.ps1— recursive-descent parser for mappings, sequences, and scalars.ConvertTo-YamlNode.ps1— recursive emitter with quoting safety (Test-YamlPlainSafe,Format-YamlString,Format-YamlDoubleQuoted).Get-PSModuleTestfunction andPSModuleTest.Tests.ps1.tests/ConvertFrom-Yaml.Tests.ps1andtests/ConvertTo-Yaml.Tests.ps1, all passing locally. Includes scalars, mappings, sequences,-AsHashtable,-NoEnumerate,-AsArray,-Indent,-EnumsAsStrings,-Depth, document markers, comments, alias registration, round-trip conversion, and explicit YAML 1.2.2 core-schema scalar resolution tests verifying thatYes/On/NULLetc. remain strings.Minorper the project's current0.0.xversioning.