Skip to content

ConvertFrom-Yaml doesn't enumerate top-level sequences in pipeline output #201

@skaravos

Description

@skaravos

Summary

When ConvertFrom-Yaml parses a top-level YAML sequence (array), the result is written to the pipeline as a single Generic.List1[Object] rather than unrolling into individual elements. This breaks direct piping to downstream cmdlets like Select-Object or Where-Object.

Expected Behaviour

# enumerable (top-level) input sequences should unroll like ConvertFrom-Json
> '[1,2,3]' | ConvertFrom-Json | Select-Object -Last 1 
3

# enumerable (top-level) input sequences should unroll like ConvertFrom-Json
> '[1,2,3]' | ConvertFrom-Yaml | Select-Object -Last 1 
3

Actual Behaviour

# enumerable input sequences don't unroll, they are passed down the pipeline as one list object
> '[1,2,3]' | ConvertFrom-Yaml | Select-Object -Last 1
1
2
3

Workarounds

NOTE: this appears to be the "intended" use of the module as all examples in the README store output into an intermediate variable

# storing output into a variable lets PowerShell unroll the list
> $x = '[1,2,3]' | ConvertFrom-Yaml
> $x | Select-Object -Last 1
3

# collecting output with parentheses also works
> ('[1,2,3]' | ConvertFrom-Yaml) | Select-Object -Last 1 
3

# inject an empty trailing YAML document (I think this difference in behavior is unintended)
> "[1,2,3]`n---" | ConvertFrom-Yaml -AllDocuments | Select-Object -Last 1 
3

Suspected cause

I think the issue lies on powershell-yaml:493

# powershell-yaml.psm1

function Convert-YamlSequenceToArray {
    # ...
    $ret = [System.Collections.Generic.List[object]](New-Object 'System.Collections.Generic.List[object]')
    # ...
    return , $ret #<-- explicitly prevents unrolling (I think to preserve nested lists??)
    # ...
}
# ...
function Convert-YamlDocumentToPSObject {
    # ...
    'YamlDotNet.RepresentationModel.YamlSequenceNode' {
        return Convert-YamlSequenceToArray $Node -Ordered:$Ordered
    }
    # ...
}
# ...
function ConvertFrom-Yaml {
    # ...
    if (($documents.Count -eq 1) -or !$AllDocuments) {
        return Convert-YamlDocumentToPSObject $documents[0].RootNode -Ordered:$Ordered  #<-- directly returns List[Object] without any unrolling, even for the top-level sequence
    }
    # with more than one document and the `-AllDocuments` switch, it assigns to an intermediate variable, so it does unroll the lists
    $ret = @()
    foreach ($i in $documents) {
        $ret += Convert-YamlDocumentToPSObject $i.RootNode -Ordered:$Ordered
    }
    return $ret
    # ...
}

Possible fix?

Enumerate the list/array results at the ConvertFrom-Yaml cmdlet boundary by either explicitly iterating, by adding parentheses around the Convert-YamlDocumentToPSObject call, or by storing the list into an intermediate variable (which allows powershell to implicitly unroll it).

function ConvertFrom-Yaml {
 # ...
  if (($documents.Count -eq 1) -or !$AllDocuments) {
     $result = Convert-YamlDocumentToPSObject $documents[0].RootNode -Ordered:$Ordered #<-- implicitly unrolls top-level sequence
     return $result
  }
  # ...
}

I applied the above change on my machine and it seems to have fixed the pipeline output for the few YAML arrays I was working with, but I obviously haven't tested all the other varieties of YAML documents, where I'm sure there are edge-cases I'm overlooking.

# with the above change to ConvertFrom-Yaml applied, arrays can be iterated in the pipeline:
> '[1,2,3]' | ConvertFrom-Yaml | Select-Object -Last 1 
3

# and nested arrays also appear to work
> '[[9,8,7],[6,5,4],[3,2,1]]' | ConvertFrom-Yaml | Select-Object -Last 1 
3
2
1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions