Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -737,14 +738,18 @@ protected override void ProcessRecord()
// Write property information
string properties = _helper.ConvertPropertyNamesCSV(_propertyNames);
if (!properties.Equals(string.Empty))
{
WriteCsvLine(properties);
}
}

string csv = _helper.ConvertPSObjectToCSV(InputObject, _propertyNames);

// write to the console
// Write to the output stream
if (csv != string.Empty)
{
WriteCsvLine(csv);
}
}

#endregion Overrides
Expand Down Expand Up @@ -908,16 +913,36 @@ internal static IList<string> BuildPropertyNames(PSObject source, IList<string>
throw new InvalidOperationException(CsvCommandStrings.BuildPropertyNamesMethodShouldBeCalledOnlyOncePerCmdletInstance);
}

// serialize only Extended and Adapted properties..
PSMemberInfoCollection<PSPropertyInfo> srcPropertiesToSearch =
new PSMemberInfoIntegratingCollection<PSPropertyInfo>(
propertyNames = new Collection<string>();
if (source.BaseObject is IDictionary dictionary)
{
foreach (var key in dictionary.Keys)
{
propertyNames.Add(LanguagePrimitives.ConvertTo<string>(key));
}

// Add additional extended members added to the dictionary object, if any
var propertiesToSearch = new PSMemberInfoIntegratingCollection<PSPropertyInfo>(
source,
PSObject.GetPropertyCollection(PSMemberViewTypes.Extended | PSMemberViewTypes.Adapted));
PSObject.GetPropertyCollection(PSMemberViewTypes.Extended));

propertyNames = new Collection<string>();
foreach (PSPropertyInfo prop in srcPropertiesToSearch)
foreach (var prop in propertiesToSearch)
{
propertyNames.Add(prop.Name);
}
}
else
{
propertyNames.Add(prop.Name);
// serialize only Extended and Adapted properties.
PSMemberInfoCollection<PSPropertyInfo> srcPropertiesToSearch =
new PSMemberInfoIntegratingCollection<PSPropertyInfo>(
source,
PSObject.GetPropertyCollection(PSMemberViewTypes.Extended | PSMemberViewTypes.Adapted));

foreach (PSPropertyInfo prop in srcPropertiesToSearch)
{
propertyNames.Add(prop.Name);
}
}

return propertyNames;
Expand Down Expand Up @@ -1014,11 +1039,26 @@ internal string ConvertPSObjectToCSV(PSObject mshObject, IList<string> propertyN
_outputString.Append(_delimiter);
}

// If property is not present, assume value is null and skip it.
if (mshObject.Properties[propertyName] is PSPropertyInfo property)
string value = null;
if (mshObject.BaseObject is IDictionary dictionary)
{
var value = GetToStringValueForProperty(property);
if (dictionary.Contains(propertyName))
{
value = dictionary[propertyName].ToString();
}
else if (mshObject.Properties[propertyName] is PSPropertyInfo property)
{
value = GetToStringValueForProperty(property);
}
}
else if (mshObject.Properties[propertyName] is PSPropertyInfo property)
{
value = GetToStringValueForProperty(property);
}

// If value is null, assume property is not present and skip it.
if (value != null)
{
if (_quoteFields != null)
{
if (_quoteFields.TryGetValue(propertyName, out _))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Describe "ConvertTo-Csv" -Tags "CI" {

It "Should output an array of objects" {
$result = $testObject | ConvertTo-Csv
,$result | Should -BeOfType System.Array
$result.GetType() | Should -Be ([object[]])
}

It "Should return the type of data in the first element of the output array" {
Expand All @@ -59,16 +59,16 @@ Describe "ConvertTo-Csv" -Tags "CI" {
}

It "Should return the column info in the second element of the output array" {
$result = $testObject | ConvertTo-Csv -IncludeTypeInformation
$result = $testObject | ConvertTo-Csv -IncludeTypeInformation

$result[1] | Should -Match "`"FirstColumn`""
$result[1] | Should -Match "`"SecondColumn`""
$result[1] | Should -Match "`"SecondColumn`""
}

It "Should return the data as a comma-separated list in the third element of the output array" {
$result = $testObject | ConvertTo-Csv -IncludeTypeInformation
$result[2] | Should -Match "`"Hello`""
$result[2] | Should -Match "`"World`""
$result[2] | Should -Match "`"World`""
}

It "Includes type information when -IncludeTypeInformation is supplied" {
Expand Down Expand Up @@ -109,7 +109,7 @@ Describe "ConvertTo-Csv" -Tags "CI" {
$result[0] | Should -BeExactly "`"FirstColumn`",SecondColumn"
$result[1] | Should -BeExactly "`"Hello`",World"

$result = $testObject | ConvertTo-Csv -QuoteFields FiRstCoLumn,SeCondCoLumn -Delimiter ','
$result = $testObject | ConvertTo-Csv -QuoteFields FiRstCoLumn, SeCondCoLumn -Delimiter ','

$result[0] | Should -BeExactly "`"FirstColumn`",`"SecondColumn`""
$result[1] | Should -BeExactly "`"Hello`",`"World`""
Expand Down Expand Up @@ -160,4 +160,36 @@ Describe "ConvertTo-Csv" -Tags "CI" {
$result[1] | Should -BeExactly "Hellor"
}
}

Context 'Converting IDictionary Objects' {
BeforeAll {
$Letters = 'A', 'B', 'C', 'D', 'E', 'F'
$Items = 0..5 | ForEach-Object {
[ordered]@{ Number = $_; Letter = $Letters[$_] }
}
$CsvString = $Items | ConvertTo-Csv
}

It 'should treat dictionary entries as properties' {
$CsvString[0] | Should -MatchExactly ($Items[0].Keys -join '","')

for ($i = 0; $i -lt $Items.Count; $i++) {
# Index in the CSV strings will be +1 due to header line
$ValuesPattern = $Items[$i].Values -join '","'
$CsvString[$i + 1] | Should -MatchExactly $ValuesPattern
}
}

It 'should ignore regular object properties' {
$PropertyPattern = $Items[0].PSObject.Properties.Name -join '|'
$CsvString[0] | Should -Not -Match $PropertyPattern
}

It 'should account for extended properties added deliberately' {
$Items | Add-Member -MemberType NoteProperty -Name 'Extra' -Value 'Surprise!'
$NewCsvString = $Items | ConvertTo-Csv
$NewCsvString[0] | Should -MatchExactly 'Extra'
$NewCsvString | Select-Object -Skip 1 | Should -MatchExactly 'Surprise!'
}
}
}