Modify xml while preserving whitespace - powershell

I'm running into several problems trying to replace an attribute in an XML file while preserving whitespace.
Attempt 1
$xml = [xml](get-content data.xml)
$xml.Path.To.Attribute = $value
set-content data.xml [String]$value
Result: Insigificant whitespace (namely newlines) are removed
Attempt 2
$xml = new-object xml
$xml.PreserveWhitespace = true
$xml.PreserveWhitespace
Result: PreserveWhitespace remains false
Attempt 3
$xml = get-content data.xml
$xml = [regex]::replace($xml, "pattern", "replacement")
set-content data.xml $xml
Result: [regex]::replace messes up the line endings
Am I taking crazy pills here?

The problems were all related: Get-Content returns lines of the text file, not the text itself. When cast back to a string, the lines are combined outright.
The best solution was to use:
$xml = [xml]([System.IO.File]::ReadAllText("data.xml"))

This isn't working because PreserveWhiteSpace is a boolean:
$xml = new-object xml
$xml.PreserveWhitespace = true
$xml.PreserveWhitespace
Use:
xml.PreserveWhitespace = $true

By default empty lines are ignored, in order to preserve them you can change PreserveWhitespace property before reading the file:
Create XmlDocument object and configure PreserveWhitespace:
$xmlDoc = [xml]::new()
$xmlDoc.PreserveWhitespace = $true
Load the document:
$xmlDoc.Load($myFilePath)
or
$xmlDoc.LoadXml($(Get-Content $myFilePath -Raw))

Related

Powershell insert XML here-string into xml document

I'm trying to insert an XML here-string into a XMLdocument. However, the saved XMLdoc, shows: "System.Xml.XmlDocument", not the content. How can I fix this?
[xml] $Doc = New-Object System.Xml.XmlDocument
$updateNode = [xml] "<Update>
<Request>Test</Request>
</Update>"
#Create XML declaration
$declaration = $Doc.CreateXmlDeclaration("1.0","UTF-8",$null)
#Append XML declaration
$Doc.AppendChild($declaration)
#Create root element
$root = $Doc.CreateNode("element","BrowseDetailsRequest",$null)
#Create node based on Here-String
$node = $Doc.CreateElement("element",$updateNode,$null)
#Append node
$root.AppendChild($node)
#Append root element
$Doc.AppendChild($root)
Output at this moment:
<?xml version="1.0" encoding="UTF-8"?>
<BrowseDetailsRequest>
<System.Xml.XmlDocument />
</BrowseDetailsRequest>
You don't really manipulate the text in the xml. Use the objects to manipulate the xml. So you need to create an element for update and request and then assign the innertext value of request.
$Doc = New-Object System.Xml.XmlDocument
$declaration = $Doc.CreateXmlDeclaration("1.0","UTF-8",$null)
$Doc.AppendChild($declaration)
$root = $Doc.CreateNode("element","BrowseDetailsRequest",$null)
$elUpdate = $doc.CreateElement("element","Update",$null)
$elRequest = $doc.CreateElement("element","Request",$null)
$elRequest.InnerText = "Test"
$elUpdate.AppendChild($elRequest)
$root.AppendChild($elUpdate)
$doc.AppendChild($root)

I cannot update ini file. Treating ini file as hastable in Powershell

I need to update ini configuration file. I managed to convert the file to hastable and updating values. But when I check if the changes are correct in the file, it hasn't changed. Add-Content doesn't work. do I need to convert to String to use Add-Content function?
Configuration file is filled with plain text also.
"ini" Configuration file:
[sqlScript1Deployment]
sqlServerName = '??????????'
olapServerName = '??????????'
(...)
My ps1 code:
[hashtable]$ht = Get-Configuration($iniFilepath)
$ht["sqlScript1Deployment"]["sqlServerName"] = 'Master'
$ht | Add-Content $iniFilepath
Expected code in "ini" file:
[sqlScript1Deployment]
sqlServerName = 'Master'
Actual result in "ini" file:
[sqlScript1Deployment]
sqlServerName = '??????????'
I have no idea where you got the Get-Configuration function from, but if it creates a hashtable where each Key is a Section for the INI and every Value is a name/value pair like this:
$ht = #{
'sqlScript1Deployment' = #{
'sqlServerName' = '??????????'
'olapServerName' = '??????????'
}
}
The following code may help:
# set the new value for sqlServerName
$ht['sqlScript1Deployment']['sqlServerName'] = 'Master'
# write the Hashtable back to disk as .INI file
$sb = New-Object -TypeName System.Text.StringBuilder
# the Keys are the Sections in the Ini file
# the properties are name/value pairs within these keys
foreach ($section in $ht.Keys) {
[void]$sb.AppendLine("[$section]")
foreach ($name in $ht[$section].Keys) {
$value = $ht[$section][$name]
# the value needs to be quoted when:
# - it begins or ends with whitespace characters
# - it contains single or double quote characters
# - it contains possible comment characters ('#' or ';')
if ($value -match '^\s+|[#;"'']|\s+$') {
# escape quotes inside the value and surround the value with double quote marks
$value = '"' + ($value -replace '(["''])', '\$1') + '"'
}
[void]$sb.AppendLine("$name = $value")
}
}
$sb.ToString() | Out-File $iniFilepath
[void]$sb.Clear()
The resulting file will look like this:
[sqlScript1Deployment]
sqlServerName = Master
olapServerName = ??????????

Using variables inside PowerShell replace

I'm trying to add some new settings to a tomcat server.xml file datasource. I can match the last setting in the datasource, which has a password that I need to capture, but when I try to replace it, I'm not see any changes.
$serverXml = "C:\server.xml"
$xml = Get-Content $serverXml
$password = (($xml -match " password=""(.*)""").Replace(' password="', "").Replace('" />', ''))[0]
$oldString = #"
username="cf.user"
password="$password" />
"#
$newString = #"
username="cf.user"
password="$password"
testWhileIdle="true"
testOnBorrow="true"
testOnReturn="false"
validationQuery="select 1"
validationInterval="30000"
minEvictableIdleTimeMillis="30000" />
"#
$xml = $xml.replace($oldString, $newString)
Set-Content -Path $serverXml -Value $xml
I'm able to match the $password fine, but when I'm using it as a variable to pass into $oldString and $newString in the replace, its not matching anymore. Even $xml -match $oldString doesn't return anything, but totally should as far as I can tell.
Do not edit XML via string replacements. Use the gratuitous XML parser PowerShell provides you with.
Load the config file like this:
[xml]$xml = Get-Content $serverXml
or like this:
$xml = New-Object Xml.XmlDocument
$xml.Load($serverXml)
The latter is a bit more failsafe, because it will (for instance) check that the encoding of the file actually matches the encoding specified in the preamble.
Select nodes via XPath expressions:
$node = $xml.SelectSingleNode('/Path/To/Node')
Change existing attributes like this:
$node.Attributes['password'] = 'newpassword'
Add new attributes like this:
$attr = $xml.CreateAttribute('testWhileIdle')
$attr.Value = 'true'
[void]$node.Attributes.Append($attr)
Then save the modified XML back to the file:
$xml.Save($serverXml)

cutting a portion of url in powershell

I have a few URLs which would need to cut and separate the first part of the each URL, i.e example1.com, example2.com, example3.com from each line and store in a variable
Contents in url.csv
https://example1.com/v1/test/f3de-a8c6-464f-8166-9fd4
https://example2.com/v1/test/14nf-d7jc-54lf-fd90-fds8
https://example3.com/v1/test/bd38-17gd-2h65-0j3b-4jf6
Script:
$oldurl = Import-CSV "url.csv"
$newurl = $oldurl.list -replace "https://"
This would replace https://, however the rest of each cannot be hard coded as those values can change.
What could be change code change required to cut anything from and after /v1/ along with https://?
$list = #(
"https://example1.com/v1/test/f3de-a8c6-464f-8166-9fd4",
"https://example2.com/v1/test/14nf-d7jc-54lf-fd90-fds8",
"https://example3.com/v1/test/bd38-17gd-2h65-0j3b-4jf6"
)
$result = $list | %{
$uri = [System.Uri] $_
$uri.Authority
}
$result
See System.Uri properties to potentially assemble the information you need in your result list.
This will cut off anything after "/v1/" and it self. Is that what you want?
$string = "https://example1.com/v1/test/f3de-a8c6-464f-8166-9fd4"
$string = $string -replace "https://"
$pos = $string.IndexOf("/v1/")
$result = $string.Substring(0, $pos)
$result
Output: example1.com

How to properly string replace in Powershell without appending the replaced variable to a newline?

I'm pretty new to powershell/programming so bear with me. I have this bug that appends the new renamed path to a new-line without the rest of path.
The console output:
/content/pizza/en/ingredients/
helloworld/menu-eng.html
What I want:
/content/pizza/en/ingredients/helloworld/menu-eng.html
What the code below is supposed to do is rename a bunch paths. Right now testName is hard-coded but after I get this to work properly it will be dynamic.
My code:
$testName = "helloworld"
$text = (Get-Content W:\test\Rename\rename.csv) | Out-String
$listOfUri = Import-Csv W:\test\Rename\rename.csv
foreach ($element in $listOfUri) {
if ($element -match "menu-eng.html") {
$elementString = $element.'ColumnTitle' | Out-String
$elementString = $elementString.Replace('menu-eng.html', '')
$varPath1 = $elementString
$elementString = $elementString.Insert('', 'http://www.pizza.com')
$elementName = ([System.Uri]$elementString).Segments[-1]
$elementString = $elementString.Replace($elementName, '')
$elementString = $elementString.Replace('http://www.pizza.com', '')
$varPath2 = $elementString.Insert($elementString.Length, $testName + '/')
$text = $text.Replace($varPath1.Trim(), $varPath2)
}
}
$text
Assuming your .csv file looks like this:
ColumnTitle,Idk
/content/pizza/en/ingredients/SPAM/menu-eng.html,Stuff
Then:
$testName = 'helloworld'
foreach ($row in Import-CSV d:\rename.csv) {
$bit = $row.'ColumnTitle'.Split('/')[-2]
$row.'ColumnTitle'.replace($bit, $testName)
}
I have no real idea what all the rest of your code is for, particularly my earlier comment, your line:
$text = (Get-Content W:\test\Rename\rename.csv) | Out-String
is making $text into an /array/ of all the lines in the file, including the headers. You can still use .Replace() on it in PowerShell, but it's going to do the replace on every line. I can't quite see how that gives you the output you get, but it will give you multiple lines for every line in the input file.