I am trying to update the sharepoint 2013 refiner webpart thru the below powershell script:
$spweb = Get-SPWeb "http://da-server/search/"
$page = $spweb.GetFile("Pages/results.aspx")
$page.CheckOut()
# Find the Refinement web part
$webPartManager = $spweb.GetLimitedWebPartManager("http://da- server/search/Pages/results.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$rwp = $webpartmanager.webparts | ? { $_.title -eq 'Refinement' }
# Refiners are updated by changing the JSON
$j = $rwp.SelectedRefinementControlsJson | ConvertFrom-Json
$j.refinerConfigurations | % { if ($_.propertyName -eq 'LastModifiedTime') { $_.displayName = 'Mod Time'; }
**$rwp.SelectedRefinementControlsJson = ConvertTo-Json $j -Compress** This line throwing the error.
# Save our changes
$webpartmanager.SaveChanges($rwp) # save changes to webpart
$page.CheckIn('Changed last mod refiner')
$page.Publish('Changed last mod refiner')
The above highlighted line 10 throwing the below error:
Exception setting "SelectedRefinementControlsJson": No parameterless constructor defined for type of 'Micrososft.Office.Server.Search.WebControls.RefinementControl[]'"
Try ConvertTo-Json $j -Depth 3
Out of box, Modified By property has more than one aliases and it's a nested level. By default ConvertTo-Json only goes 2 level. So you have to use -depth to convert it properly.
I guess you have in one or more refiners only one alias defined. In this case the PS ConvertTo-Json function converts the array to a string. More details found here https://superuser.com/questions/414650/why-does-powershell-silently-convert-a-string-array-with-one-item-to-a-string
As I struggled with the same issue here is my solution
Function ConvertTo-Json {
param($inputObject)
if ($inputObject -eq $null) { "null" }
else {
switch ($inputObject.GetType().Name) {
"String" { '"' + $inputObject +'"' }
"Boolean" {
if($inputObject){
"true"
}
else {
"false"
}
}
"Object[]" {
$items = #()
$inputObject | % {
$items += ConvertTo-Json $_
}
$ofs = ","; "[" + [string]$items + "]"
}
"PSCustomObject" {
$properties = #()
$inputObject | Get-Member -MemberType *Property | % {
$properties += '"'+ $($_.Name) + '":' + $(ConvertTo-Json $inputObject.($_.Name))
}
$ofs = ","; "{" + [string]$properties + "}"
}
default { $inputObject }
}
}
}
Now it can be used as before, but without -Compress parameter
$rwp.SelectedRefinementControlsJson = ConvertTo-Json $j
Related
I am trying to use ConvertTo-Json in PowerShell. By default, ConvertTo-Json will escape special characters. However, it will not escape chinese.
For example:
"<>中文ABC" | ConvertTo-Json
The output is
"\u003c\u003e中文ABC"
But, what I really want is "\u003c\u003e\u4e2d\u6587ABC". Can anyone share the experience?
Found a way:
function ConvertTo-Rtf
{
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline = $true)]
[string[]] $String
)
process
{
if ($null -eq $String) { $String = #() }
$stringBuilder = New-Object System.Text.StringBuilder
foreach ($s in $String)
{
$stringBuilder.Length = 0
foreach ($character in $s.GetEnumerator())
{
if ([int]$character -lt 0x7F)
{
$null = $stringBuilder.Append($character)
}
else
{
$null = $stringBuilder.AppendFormat('\u{0:x}', [int]$character)
}
}
$stringBuilder.ToString()
}
}
}
Then #{name="ABC<中文>" } | ConvertTo-Json -Compress| ConvertTo-Rtf
Output:
{"name":"ABC\u003c\u4e2d\u6587\u003e"}
Hello I am new to powershell.
I am trying to create a directory structured into json format using powershell. similar to these picture below
Example json format of directory structure
I have researched some ways into doing this and found this, is their a similar or better way into naming directory and files into json format.
function Add-Tabstops{
param($Count)
$tabs = ""
for($i=0; $i -lt $Count; $i++){$tabs += " "}
return $tabs
}
function Output-JsonChildren{
param($Path, $Level = 1)
return $(Get-ChildItem -Path $Path | Where-Object{$_} | ForEach-Object{
(Add-Tabstops $Level) +
"{`n" +
(Add-Tabstops ($Level+1)) +
"`"name`"`: `"$($_.Name)`"," +
"`n" +
(Add-Tabstops ($Level+1)) +
"`"children`": ["+
$(if($_.psiscontainer){"`n" + (Output-JsonChildren -Path $_.FullName -Level ($Level+2))+ "`n" + (Add-Tabstops ($Level+1))}) +
"]`n" +
(Add-Tabstops ($Level)) +
"}"
}) -join ",`n"
}
$JSON = Output-JsonChildren -Path "C:\Users\Glen\Desktop\democontainer" | Out-File "C:\Users\Glen\Desktop\democontainer\test.json"
"["
$JSON
"]"
Something like this:
function build-json {
param (
[string]$fullPath
)
$hashtable = #{}
$directories = get-childitem $fullPath -Directory
try{
$files = (get-childitem $fullPath -File)
if($null -eq $files) {
$hashtable.files = #()
}
else {
$hashtable.files = [array]$files.Name
}
}
catch {
echo 'xxx'
}
$hashtable.directories = #{}
foreach($directory in $directories) {
$element = Split-Path $directory.fullname -Leaf
$hashtable.directories.$element = build-json -fullPath $directory.FullName -hashtable $hashtable
}
return $hashtable
}
$sourcefolder = 'E:\xxx\Software\yyy\python392'
$result3 = build-json -fullPath $sourcefolder -hashtable #{}
Yes, it's possible.
You will need to write a little recursive function which walks through the dictionaries and collects the information for you.
You can use a hashtable (#{}) or a PowerShell object, but a hashtable is preferable.
Get-ChildItem and ConvertTo-Json are your friends
I built a recursive solution to read in a JSON doc and output a flattened table. Now I want to reverse the operation but am having a hard time figuring out a soliton. I need it to be in PowerShell as I just want to add it to my existing module.
I posted about my flatten code here: https://stackoverflow.com/a/63499156/13224569
I have a partial solution, but it falling well short. Here's that code:
$a = '.person.pets[0].color.black.toy.foo' -split '\.'
$b = '.john.harold.cravener' -split '\.'
$z = '.john.is.content' -split '\.'
$m = #{}
$c = #{}
$p = #{}
$pk = $null
$firstTime = $true
foreach($i in $a[1..($a.Count-1)]) {
$c = #{$i = $null}
if($firstTime){
$m = $c
$firstTime = $false
}
if($p -and $pk) {
$p[$pk] = $c
}
$p = $c
$pk = $i
}
$m
exit
And here's its output:
PS D:\> .\tester.ps1 | ConvertTo-Json -Depth 20
{
"person": {
"pets[0]": {
"color": {
"black": {
"toy": {
"foo": null
}
}
}
}
}
}
My first challenge, I can't figure out how to move on to the next row and store it to the exiting $m variable. I'm getting tripped up with the way this var is being referenced in PowerShell / .Net Core.
The second challenge is how to deal with arrays, but I haven't even started on this part yet.
Any and all help / suggestions would be greatly appreciated!
After some research, and a lot of work and debugging, I came to a solution. From my testing so far, it works quite well. It's based on simonw's python code here. And while my solution needed to be in PowerShell, it's not a straight port but pretty close. Most of the modifications are based on differences in how PowerShell and Python handles certain things. The main function is ConvertFrom-JhcUtilJsonTable which in turn depends on three helper functions. Here's all of them.
function ConvertFrom-JhcUtilJsonTable {
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[System.Collections.Hashtable]
$jsonHashTable
)
begin {}
process {
foreach ($h in $jsonHashTable) {
$m = #{}
foreach ($k in $h.Keys) {
$current = $m
$val = $h[$k]
$k = ($k -replace '\]', '') -replace '\[', '.'
$bits = $k.split('.')
$path = $bits[1..($bits.Count - 1)]
$count = 0
foreach ($bit in $path) {
$count++
if ($v = $current.item($bit)) {
$current[$bit] = $v
}
else {
if ($count -eq $path.Count) {
$current[$bit] = $val
}
else {
$current[$bit] = #{}
}
}
$current = $current[$bit]
}
}
intKeyHashToLists -obj $m
#--python code had a buit about handling root units e.g. {'$empty': '{}'} - need to add that
}
}
end {}
}
#---helper function for ConvertFrom-JhcUtilJsonTable
#
function isType {
param (
[Parameter(Mandatory)]
[System.Object]
$obj,
[Parameter(Mandatory)]
[ValidateSet('Hashtable', 'Object[]')]
[System.String]
$typeName
)
$t = $obj.GetType()
if ($t.Name -eq $typeName) {
return $true
}
return $false
}
#---helper function for ConvertFrom-JhcUtilJsonTable
#
function allKeysDigits {
param (
[Parameter(Mandatory)]
[System.Collections.Hashtable]
$h
)
foreach ($k in $h.Keys) {
if ($k -match '^0\d') {
return $false
}
if ($k -notmatch '^\d+$') {
return $false
}
}
return $true
}
#---helper function for ConvertFrom-JhcUtilJsonTable
#
function intKeyHashToLists {
param (
[Parameter(Mandatory)]
[System.Object]
$obj
)
if (isType -obj $obj -typeName 'Hashtable') {
if ($obj -and (allKeysDigits -h $obj)) {
$a = #()
foreach ($k in ($obj.Keys | Sort-Object) ) {
$a += intKeyHashToLists -obj $obj.item($k)
}
return ,$a #--- adding the comma forces this to retun an array even when it's a single element
}
else {
$h = #{}
foreach ($k in $obj.Keys) {
$h[$k] = intKeyHashToLists -obj $obj.item($k)
}
return $h
}
}
elseif (isType -obj $obj -typeName 'Object[]') {
return ( $obj | ForEach-Object { intKeyHashToLists -obj $_ } )
}
else {
return $obj
}
}
What is the easiest way to convert a PSCustomObject to a Hashtable? It displays just like one with the splat operator, curly braces and what appear to be key value pairs. When I try to cast it to [Hashtable] it doesn't work. I also tried .toString() and the assigned variable says its a string but displays nothing - any ideas?
Shouldn't be too hard. Something like this should do the trick:
# Create a PSCustomObject (ironically using a hashtable)
$ht1 = #{ A = 'a'; B = 'b'; DateTime = Get-Date }
$theObject = new-object psobject -Property $ht1
# Convert the PSCustomObject back to a hashtable
$ht2 = #{}
$theObject.psobject.properties | Foreach { $ht2[$_.Name] = $_.Value }
Keith already gave you the answer, this is just another way of doing the same with a one-liner:
$psobject.psobject.properties | foreach -begin {$h=#{}} -process {$h."$($_.Name)" = $_.Value} -end {$h}
Here's a version that works with nested hashtables / arrays as well (which is useful if you're trying to do this with DSC ConfigurationData):
function ConvertPSObjectToHashtable
{
param (
[Parameter(ValueFromPipeline)]
$InputObject
)
process
{
if ($null -eq $InputObject) { return $null }
if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string])
{
$collection = #(
foreach ($object in $InputObject) { ConvertPSObjectToHashtable $object }
)
Write-Output -NoEnumerate $collection
}
elseif ($InputObject -is [psobject])
{
$hash = #{}
foreach ($property in $InputObject.PSObject.Properties)
{
$hash[$property.Name] = ConvertPSObjectToHashtable $property.Value
}
$hash
}
else
{
$InputObject
}
}
}
My extremely lazy approach, enabled by a new feature in PowerShell 6:
$myhashtable = $mypscustomobject | ConvertTo-Json | ConvertFrom-Json -AsHashTable
This works for PSCustomObjects created by ConvertFrom_Json.
Function ConvertConvertFrom-JsonPSCustomObjectToHash($obj)
{
$hash = #{}
$obj | Get-Member -MemberType Properties | SELECT -exp "Name" | % {
$hash[$_] = ($obj | SELECT -exp $_)
}
$hash
}
Disclaimer: I barely understand PowerShell so this is probably not as clean as it could be. But it works (for one level only).
My code:
function PSCustomObjectConvertToHashtable() {
param(
[Parameter(ValueFromPipeline)]
$object
)
if ( $object -eq $null ) { return $null }
if ( $object -is [psobject] ) {
$result = #{}
$items = $object | Get-Member -MemberType NoteProperty
foreach( $item in $items ) {
$key = $item.Name
$value = PSCustomObjectConvertToHashtable -object $object.$key
$result.Add($key, $value)
}
return $result
} elseif ($object -is [array]) {
$result = [object[]]::new($object.Count)
for ($i = 0; $i -lt $object.Count; $i++) {
$result[$i] = (PSCustomObjectConvertToHashtable -object $object[$i])
}
return ,$result
} else {
return $object
}
}
For simple [PSCustomObject] to [Hashtable] conversion Keith's Answer works best.
However if you need more options you can use
function ConvertTo-Hashtable {
<#
.Synopsis
Converts an object to a hashtable
.DESCRIPTION
PowerShell v4 seems to have trouble casting some objects to Hashtable.
This function is a workaround to convert PS Objects to [Hashtable]
.LINK
https://github.com/alainQtec/.files/blob/main/src/scripts/Converters/ConvertTo-Hashtable.ps1
.NOTES
Base ref: https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/turning-objects-into-hash-tables-2
#>
PARAM(
# The object to convert to a hashtable
[Parameter(ValueFromPipeline = $true, Mandatory = $true)]
$InputObject,
# Forces the values to be strings and converts them by running them through Out-String
[switch]$AsString,
# If set, empty properties are Included
[switch]$AllowNulls,
# Make each hashtable to have it's own set of properties, otherwise,
# (default) each InputObject is normalized to the properties on the first object in the pipeline
[switch]$DontNormalize
)
BEGIN {
$headers = #()
}
PROCESS {
if (!$headers -or $DontNormalize) {
$headers = $InputObject | Get-Member -type Properties | Select-Object -expand name
}
$OutputHash = #{}
if ($AsString) {
foreach ($col in $headers) {
if ($AllowNulls -or ($InputObject.$col -is [bool] -or ($InputObject.$col))) {
$OutputHash.$col = $InputObject.$col | Out-String -Width 9999 | ForEach-Object { $_.Trim() }
}
}
} else {
foreach ($col in $headers) {
if ($AllowNulls -or ($InputObject.$col -is [bool] -or ($InputObject.$col))) {
$OutputHash.$col = $InputObject.$col
}
}
}
}
END {
return $OutputHash
}
}
Maybe this is overkill but I hope it Helps
Today, the "easiest way" to convert PSCustomObject to Hashtable would be so:
$custom_obj | ConvertTo-HashtableFromPsCustomObject
OR
[hashtable]$custom_obj
Conversely, you can convert a Hashtable to PSCustomObject using:
[PSCustomObject]$hash_table
Only snag is, these nifty options may not be available in older versions of PS
I am trying to write a recursive function that will return information in an array, however when I put a return statement into the function it misses certain entries.
I am trying to recursively look through a specified depth of folders getting the acl's associated with the folder. I know getChildItem has a recurse option, but I only want to step through 3 levels of folders.
The excerpt of code below is what I have been using for testing. When getACLS is called without a return statement (commented out below) the results are:
Folder 1
Folder 12
Folder 13
Folder 2
When the return statement is used I get the following output:
Folder 1
Folder 12
So it looks like the return statement is exiting out from the recursive loop?
The idea is that I want to return a multidimensional array like [folder name, [acls], [[subfolder, [permissions],[[...]]]]] etc.
cls
function getACLS ([string]$path, [int]$max, [int]$current) {
$dirs = Get-ChildItem -Path $path | Where { $_.psIsContainer }
$acls = Get-Acl -Path $path
$security = #()
foreach ($acl in $acls.Access) {
$security += ($acl.IdentityReference, $acl.FileSystemRights)
}
if ($current -le $max) {
if ($dirs) {
foreach ($dir in $dirs) {
$newPath = $path + '\' + $dir.Name
Write-Host $dir.Name
# return ($newPath, $security, getACLS $newPath $max ($current+1))
# getACLS $newPath $max ($current+1)
return getACLS $newPath $max ($current+1)
}
}
} elseif ($current -eq $max ) {
Write-Host max
return ($path, $security)
}
}
$results = getACLS "PATH\Testing" 2 0
The problem was the location of the return. I had it inside the foreach loop, meaning it was trying to return multiple times in the one function. I moved it outside the foreach, into the if statement instead.
function getACLS ([string]$path, [int]$max, [int]$current) {
$dirs = Get-ChildItem -Path $path | Where { $_.psIsContainer }
$acls = Get-Acl -Path $path
$security = #()
$results = #()
foreach ($acl in $acls.Access) {
$security += ($acl.IdentityReference, $acl.FileSystemRights)
}
if ($current -lt $max) {
if ($dirs) {
foreach ($dir in $dirs) {
$newPath = $path + '\' + $dir.Name
$next = $current + 1
$results += (getACLS $newPath $max $next)
}
} else {
$results = ($path, $security)
}
return ($path, $security, $results)
} elseif ($current -eq $max ) {
return ($path, $security)
}
}
In recursion, I would only use a return statement where I needed to end the recursion - just for clarity. I've done a good bit of recursion in PowerShell and it works well. However you need to remember that PowerShell functions do behave differently. The following:
return 1,2
is equivalent to:
1,2
return
In other words, anything you don't capture in a variable or redirect to a file (or $null) is automatically considered output of the function. Here's a simple example of a working, recursive function:
function recursive($path, $max, $level = 1)
{
$path = (Resolve-Path $path).ProviderPath
Write-Host "$path - $max - $level"
foreach ($item in #(Get-ChildItem $path))
{
if ($level -eq $max) { return }
recursive $item.PSPath $max ($level + 1)
}
}
recursive ~ 3
Update: I am leaving the first answer as is and adding the new code here. I see that there are multiple issues with your code. here is the updated one.
cls
function getACLS ([string]$path, [int]$max, [int]$current) {
$dirs = Get-ChildItem -Path $path | Where { $_.psIsContainer }
$acls = Get-Acl -Path $path
$security = #()
foreach ($acl in $acls.Access) {
$security += ($acl.IdentityReference, $acl.FileSystemRights)
}
if ($current -lt $max) {
if ($dirs) {
foreach ($dir in $dirs) {
$newPath = $dir.FullName
$security
getACLS $newPath $max ($current+1)
}
}
} elseif ($current -eq $max ) {
Write-Host max
return $security
}
}
$results = getACLS "C:\Scripts" 2 0
If you see above, I am not using return. I just throw the object from the GetACLs function. Also, I modified it to return on $security for testing purpose. I can see the all ACLs in $results. I changed the first if condition to if ($current -lt $max). It should not be if ($current -le $max).
Let me know if this what you are looking for. I can continue to optimize this.
==========================================OLD=============================================
Return will exit the function.
I am not providing the complete solution here but want to give you an idea about how this can be changed.
You can use PS Custom object to capture the information you need. For example,
function GetItem {
$itemsArray = #()
Get-ChildItem C:\Scripts | ForEach-Object {
$itemsObject = New-Object PSObject
Add-Member -InputObject $itemsObject -MemberType NoteProperty -Name "FullName" -Value $_.FullName
Add-Member -InputObject $itemsObject -MemberType NoteProperty -Name "Name" -Value $_.Name
$itemsArray += $itemsObject
}
return $itemsArray
}
This way you can return the object once it is completely built with the information you need.
I found none of these solutions did what I need to, which was to have an array with the results of the whole recursive function. Since we can't initialise the array inside the function, otherwise it is re-initialised every time recursion is used, we have to define it outside and use global:
# Get folder contents recursively and add to an array
function recursive($path, $max, $level = 1)
{
Write-Host "$path" -ForegroundColor Red
$global:arr += $path
foreach ($item in #(Get-ChildItem $path))
{
if ($level -eq $max) { return }
if ($item.Length -eq "1") # if it is a folder
{
$newpath = "$path\$($item.Name)"
recursive $newpath $max ($level + 1)
}
else { # else it is a file
Write-Host "$path\$($item.Name)" -ForegroundColor Blue
$global:arr +="$path\$($item.Name)"
}
}
}
$arr = #() # have to define this outside the function and make it global
recursive C:\temp\test2 4
write-host $arr.count