@@ -31,15 +31,15 @@ function ConvertTo-Cson {
3131 [AllowEmptyCollection ()]
3232 [AllowEmptyString ()]
3333 [AllowNull ()]
34- [object ]$InputObject ,
34+ [object ] $InputObject ,
3535
3636 [PSDefaultValue (Help = ' Tab' )]
37- [string ]$Indent = " `t " ,
37+ [string ] $Indent = " `t " ,
3838
3939 [ValidateRange (1 , 100 )]
40- [int32 ]$Depth = 2 ,
40+ [int32 ] $Depth = 2 ,
4141
42- [switch ]$EnumsAsStrings
42+ [switch ] $EnumsAsStrings
4343 )
4444 # write out a CSON document from the object supplied
4545 # $InputObject is an object, who's properties will be output as CSON. Hash tables are supported.
@@ -48,24 +48,20 @@ function ConvertTo-Cson {
4848
4949 # define a match evaluator for escaping characters
5050 $escape_replacer = {
51- switch ($_ ) {
52- { $_.Groups [1 ].Success } {
53- # group 1, control characters
54- switch ($_.Value [0 ]) {
55- <# appearing in order of expected frequency, from most frequent to least frequent #>
56- ([char ]10 ) { ' \n' ; continue } # new line
57- ([char ]9 ) { ' \t' ; continue } # tab
58- ([char ]13 ) { ' \r' ; continue } # caridge return
59- ([char ]12 ) { ' \f' ; continue } # new form
60- ([char ]8 ) { ' \b' ; continue } # bell
61- default { ' \u{0:X4}' -f [int16 ]$_ } # unicode escape all others
62- }
63- continue
64- }
65- { $_.Groups [2 ].Success } {
66- # group 2, items that need `\` escape
67- " \$ ( $_.Value ) "
51+ if ($_.Groups [1 ].Success) {
52+ # group 1, control characters
53+ switch ($_.Value [0 ]) {
54+ <# appearing in order of expected frequency, from most frequent to least frequent #>
55+ ([char ]10 ) { ' \n' ; continue } # new line
56+ ([char ]9 ) { ' \t' ; continue } # tab
57+ ([char ]13 ) { ' \r' ; continue } # caridge return
58+ ([char ]12 ) { ' \f' ; continue } # new form
59+ ([char ]8 ) { ' \b' ; continue } # bell
60+ default { ' \u{0:X4}' -f [int16 ]$_ } # unicode escape all others
6861 }
62+ } elseif ($_.Groups [2 ].Success) {
63+ # group 2, items that need `\` escape
64+ " \$ ( $_.Value ) "
6965 }
7066 }
7167
@@ -76,105 +72,138 @@ function ConvertTo-Cson {
7672 " "" $ ( $_ -replace ' ([\x00-\x1F\x85\u2028\u2029])|([\\"]|#\{)' , $escape_replacer ) "" "
7773 }
7874
79- function writeProperty ([string ]$name , $item , [string ]$indention , [int32 ]$level ) {
80- # writing the property may require recursively breaking down the objects based on their type
81- # name of the property is optional, but that is only intended for the first property object
75+ function writeObject ($item ) {
8276
83- function writeValue ( $item , [string ]$indention ) {
84- # write a property value
77+ function writeProperty ( [string ] $name , $value ) {
78+ # write a property name and its value, which may require recursing back to writeObject
8579 " $indention $ (
86- if (($item -is [string ]) -or ($item -is [char ]) -or (($item -is [enum ]) -and $EnumsAsStrings ) -or ($level -ge $Depth )) {
87- # handle strings or characters, or objects exceeding the max depth
88- " $item " | writeStringValue
89- }
90- elseif ($item -is [boolean ]) {
91- # handle boolean type
92- if ($item ) {
93- ' true'
94- }
95- else {
96- ' false'
97- }
98- }
99- elseif ($item -is [datetime ]) {
100- # specifically format date/time to ISO 8601
101- $item.ToString (' o' ) | writeStringValue
102- }
103- elseif ($item -isnot [enum ]) {
104- # assuming a [valuetype] that doesn't need special treatment
105- $item
106- }
107- else {
108- # specifically out the enum value
109- $item.value__
80+ # if a property name is not all simple characters or starts with numeric digit, it must be quoted and escaped
81+ if (-not $name -or $name -match ' [^\p{L}\d_]|^\d' ) {
82+ # property name requires escaping
83+ $name | writeStringValue
84+ }
85+ else {
86+ $name
87+ }
88+ ) :$ (
89+ if (($level -gt $Depth ) -or ($null -eq $value ) -or ($value -is [ValueType ]) -or ($value -is [string ])) {
90+ " $ ( , $value | writeValue) " # comma forces $value to be treated as a whole object instead of being enumerated as subitems
91+ }
92+ elseif ($value -is [Collections.IList ]) {
93+ " [$ ( # add array start token if value is an array
94+ if ($value.Count -eq 0 ) {
95+ ' ]' # add array end token if value is an empty array
96+ }
97+ ) "
98+ }
99+ ) "
100+ if ($level -le $Depth ) {
101+ # if exceeded Depth, value already written above
102+ if (($value -is [Collections.IList ]) -and ($value.Count -ne 0 )) {
103+ # handle nested non-empty arrays specially due to already emitted array start token
104+ $level ++ # level increases for arrays or objects
105+ $value | writeArray # write the nested array
106+ " $indention ]" # nested array end token
107+ } elseif ($value -and ($value -isnot [ValueType ]) -and ($value -isnot [string ])) {
108+ $indention = " $indention$Indent "
109+ writeObject $value # recurse the element to writeObject
110110 }
111- ) "
111+ }
112112 }
113113
114- # write out key name, if one was supplied from the parent object
115- if ($name ) {
116- " $indention $ (
117- # if a property name is not all simple characters or start with numeric digit, it must be quoted and escaped
118- if ($name -match ' [^\p{L}\d_]|^\d' ) {
119- # property name requires escaping
120- $name | writeStringValue
121- }
122- else {
123- $name
124- }
125- ) :$ (
126- if (($item -is [array ]) -and ($level -lt $Depth )) {
127- ' [' # add array start token if property is an array
128- }
129- elseif (($item -is [ValueType ]) -or ($item -is [string ]) -or ($level -ge $Depth )) {
130- " $ ( writeValue $item ' ' ) "
114+ filter writeValue {
115+ # write a object property or array element simple value
116+ if ($null -eq $_ ) {
117+ ' null'
118+ } elseif (($_ -is [char ]) -or ($EnumsAsStrings -and ($_ -is [enum ])) -or ($_ -isnot [ValueType ])) {
119+ # handle strings or characters, or objects exceeding the max depth
120+ " $_ " | writeStringValue
121+ } elseif ($_ -is [boolean ]) {
122+ # handle boolean type
123+ if ($_ ) {
124+ ' true'
125+ } else {
126+ ' false'
131127 }
132- ) "
133- }
134- else {
135- if (($item -is [array ]) -and ($level -lt $Depth )) {
136- " $indention [" # add array start token if property is an array
137- }
138- elseif (($item -is [valuetype ]) -or ($item -is [string ]) -or ($level -ge $Depth )) {
139- writeValue $item " $indention "
128+ } elseif ($_ -is [datetime ]) {
129+ # specifically format date/time to ISO 8601
130+ $_.ToString (' o' ) | writeStringValue
131+ } elseif ($_ -isnot [enum ]) {
132+ # assuming a [valuetype] that doesn't need special treatment
133+ $_
134+ } else {
135+ # specifically out the enum value
136+ $_.value__
140137 }
141138 }
142139
143- if ($level -lt $Depth ) {
144- if ($item -is [array ]) {
145- # handle arrays, iterate through the items in the array
146- foreach ($subitem in $item ) {
147- if (($subitem -is [valuetype ]) -or ($subitem -is [string ])) {
148- writeValue $subitem " $indention$Indent "
149- }
150- elseif ($subitem -is [array ]) {
151- writeProperty ' ' $subitem " $indention$Indent " ($level + 1 )
152- }
153- else {
154- " $indention$indent {"
155- writeProperty ' ' $subitem " $indention$Indent " ($level + 1 )
156- " $indention$Indent }"
140+ filter writeArray {
141+ begin {
142+ $indentionArray = " $indention$Indent "
143+ }
144+ process {
145+ # if depth not exceeded, check for a nested object
146+ if (($level -le $Depth ) -and $_ -and ($_ -isnot [ValueType ]) -and ($_ -isnot [string ]) -and ($_ -isnot [Collections.IList ])) {
147+ # an object is nested within the array element
148+ if ($ (if ($_ -is [Collections.IDictionary ]) { $_.get_Keys ().Count } else { @ ($_.psobject.get_Properties ()).Count } ) -gt 0 ) {
149+ " $indentionArray {" # object start token
150+ $indention = " $indentionArray$Indent "
151+ writeObject $_ # recurse the object to writeObject
152+ " $indentionArray }" # object end token
153+ } else {
154+ " $indentionArray {}" # empty object
157155 }
156+ } else {
157+ # for all other cases recurse the value back to writeObject
158+ $indention = $indentionArray
159+ writeObject $_
158160 }
159- " $indention ]"
160161 }
161- elseif ($item -isnot [valuetype ] -and $item -isnot [string ]) {
162- # handle objects by recursing with writeProperty
163- if ($item.GetType ().Name -in ' HashTable' , ' OrderedDictionary' ) {
162+ }
163+
164+ if (($level -gt $Depth ) -or ($null -eq $item ) -or ($item -is [ValueType ]) -or ($item -is [string ])) {
165+ " $indention $ ( , $item | writeValue) " # comma forces $item to be treated as a whole object instead of being enumerated as subitems
166+ } else {
167+ $level ++ # level increases for arrays or objects
168+ if ($item -is [Collections.IList ]) {
169+ if ($item.Count -ne 0 ) {
170+ # handle non-empty arrays, iterate through the items in the array
171+ " $indention ["
172+ $item | writeArray
173+ " $indention ]"
174+ } else {
175+ " $indention []" # indicate empty array
176+ }
177+ } elseif ($ (if ($item -is [Collections.IDictionary ]) { $item.get_Keys ().Count } else { @ ($item.psobject.get_Properties ()).Count } ) -gt 0 ) {
178+ if ($item -is [Collections.IDictionary ]) {
164179 # process what we assume is a hashtable object
165- foreach ($hash in $item.GetEnumerator ()) {
166- writeProperty $hash.Key $hash.Value $ (if ($level -ge 0 ) { " $indention$Indent " } else { $indention }) ($level + 1 )
180+ foreach ($key in $item.get_Keys ()) {
181+ # handle objects by recursing with writeProperty
182+ writeProperty $key $item [$key ]
167183 }
168184 } else {
169- # iterate through the items (force to a PSCustomObject for consistency)
170- foreach ($property in ([PSCustomObject ]$item ).psobject.Properties) {
171- writeProperty $property.Name $property.Value $ (if ($level -ge 0 ) { " $indention$Indent " } else { $indention }) ($level + 1 )
185+ # iterate through the objects properties
186+ foreach ($property in $item.psobject.Properties ) {
187+ # handle objects by recursing with writeProperty
188+ writeProperty $property.Name $property.Value
172189 }
173190 }
191+ } else {
192+ " $indention {}" # indicate empty object
174193 }
175194 }
176195 }
177196
178- # start writing the property list, the property list should be an object, has no name, and starts at base level
179- (writeProperty ' ' $InputObject ' ' (-1 )) -join $ (if (-not $IsCoreCLR -or $IsWindows ) { " `r`n " } else { " `n " })
197+ # start writing the input object starting with no indent at level 0
198+ [string ] $indention = ' '
199+ [int32 ] $level = 0
200+ (writeObject $ (
201+ # we need to determine where our input is coming from, pipeline or parameter argument.
202+ if ($input -is [array ] -and $input.Length -ne 0 ) {
203+ $input # input from pipeline
204+ } else {
205+ $InputObject # input from parameter argument
206+ }
207+ )
208+ ) -join " $ ( if (-not $IsCoreCLR -or $IsWindows ) { " `r " }) `n "
180209}
0 commit comments