Rectangle 27 40

If you use dot notation to navigate an XML file (e.g. $doc.configuration), Powershell tries to be clever about what it returns.

  • If the target element is empty or only contains a single text node, PS will return a String.
  • If the target element contains child nodes other than text nodes, it will return an XmlElement.
  • If multiple target elements exist, it will return an Object[], where each individual array element is again subject to these rules, e.g. it will either be a String or an XmlElement depending on its contents.
  • If the target element does not exist, PS returns $null.
$doc = New-Object System.Xml.XmlDocument
$doc.Load($filePath)
$child = $doc.CreateElement("newElement")
$doc.DocumentElement.AppendChild($child)

but you could use $doc.SelectNodes() or $doc.SelectSingleNode() to navigate around the XML document and always have a node/node list returned.

I found that if <configuration> tag is empty, $doc.configuration is String, if not empty - XmlElement. I've solved my problem using $doc.SelectSingleNode('configuration') that returns $null or XmlElement.

How to add a child element for XML in PowerShell - Stack Overflow

xml powershell
Rectangle 27 0

System.Xml.Reader
[xml]$data = Get-Content "C:\foo.xml"

$data.SelectNodes("//foo") | % {
  $_.bar
  $_.baz
} | Out-File "C:\out.txt"

Under the hood, this is basically a wrapper around the .NET XmlDocument class. I was getting OutOfMemory exceptions when loading my 200 MB, deeply nested files that way.

How to emit Windows line endings when writing element strings from XML...

xml powershell newline xmlreader
Rectangle 27 0

You could combine powershell with xpath. Assume that you have fixed XML errors and renamed Element.Name to be in same case as in data file:

your_file.xml
<root> 
  <list> 
       <FirstName>Abc</FirstName> 
       <LastName>LT</LastName> 
       <Occupation>Eng</Occupation> 
       <BirthDate></BirthDate> 
      ... 
 </list> 
</root>
index_file.xml
<root> 
    <Element Name="FirstName" /> 
    <Element Name="LastName" /> 
    <Element Name="Occupation" /> 
    <Element Name="Java" /> 
    <Element Name="NET" /> 
... 
</root>
PoSH
$xmlIndex = [xml](gc ".\index_file.xml")
$xml = [xml](gc ".\your_file.xml")
$allValues = @{}; 

$xmlIndex.SelectNodes("//Element/@Name") | 
    %{ $nodeName = $_."#text"; $xml.SelectNodes("//$nodeName/text()") } |
    % { $allvalues[$_.ParentNode.ToString()] = $_.Value };

$allValues | ft

and

Name                           Value
----                           -----
NET                            b
Java                           Ab

Hi akim, how can I use your code above with foreach loop so that I can add the returned value to a variable

$allValues = @{}; $xml.SelectNodes("//text()") | % { $allvalues[$_.ParentNode.ToString()] = $_.Value }; $allValues | ft

Hi akim, thanks a lot for your help. I just added a few things in my question above. Can you tell me how can I display the value of each element only if that element is specified in the Query XML file?

Does it work now with two XMLs?

Hi akim, above I added some code that I have done so far. I appologize for not following your example. I am using xmldocument since I found some code that was working to a point. My problem with the code is, if I have 5 xml files to add, the items of the last xml will be dupplicated. Any idea what I am doing wrong there? Thanks for your help.

c# - Iterate through xml elements with powershell - Stack Overflow

c# xml powershell xpath sharepoint-2010
Rectangle 27 0

The answer provided by Ansgar was relevant for a specific node of specific name, length (and other properties). My research have enlightened me a bit more onto powershell and the notable would be SelectNodes("//*column"). I ended up doing an itteration using .Net as I am more familiar with it.

Powershell - Removing Element within many XML files - Stack Overflow

xml powershell
Rectangle 27 0

Your xml isn't valid. XML requires a root element. If you "xml"-file is missing it, then wrap the file's content inside a root element yourself like this:

Get-ChildItem -Path C:\Users\Frode\Desktop -Filter "appcmd*" | ForEach-Object {

    #Wrap "xml" in root element and convert to xml-type
    $xml = [xml]@"
<root>
$([System.IO.File]::ReadAllText($_.FullName))
</root>
"@

    #Get first physicalPath
    $xml.root.Application[0].virtualDirectory.physicalPath
}

C:\WEB\PROD\komplus.ua
$([System.IO.File]::ReadAllText($_.FullName))
$(Get-Content -Path $_.FullName -Raw))
ReadAllText()

If you want the root-application, you should probably search for that instead of guessing that it's the first element. You could try something like this:

Get-ChildItem -Path C:\Users\Frode\Desktop -Filter "appcmd*" | ForEach-Object {

    #Wrap "xml" in root element and convert to xml-type
    $xml = [xml]@"
<root>
$(Get-Content -Path $_.FullName -Raw))
</root>
"@

    #Get root app's physicalPath
    $xml.root.Application | Where-Object { $_.path -eq '/' } | ForEach-Object { $_.virtualDirectory.physicalPath }
}

If you're getting the xml-content directly from appcmd.exe (stored in a variable), then modify like this:

$outputfromappcmd = appcmd.exe something something

#Wrap "xml" in root element and convert to xml-type
$xml = [xml]"<root>$outputfromappcmd </root>"

#Get first physicalPath
$xml.root.Application | Where-Object { $_.path -eq '/' } | ForEach-Object { $_.virtualDirectory.physicalPath }

I've tried your advise. Please see my full function code in the original quesiton's body, I've added it.

+ $xmlInput = [xml]@"<root>$input</root>"@ [Step 1/3] + ~ [Step 1/3] No characters are allowed after a here-string header but before the end of the [Step 1/3] line.

I am getting the above mentioned error in build output when I am trying your last advise. I think we are almost there, please take a look =)

You're not using my last advise properly. You can't use a here-string like that. $xmlinput = [xml]@" and "@ need to be their own lines. See how I used it in the first solution, or drop the here-string and use $xmlinput = [xml]"<root>$input</root>"

The catch was not to cast appcmd piplined $input into a variable, but to just use it right away in an xml cast. Thank you @Frode F.

xml - Get first element's attribute in Powershell - Stack Overflow

xml powershell
Rectangle 27 0

You could easily just split the messages on the new line character and then filter out lines which do not have any content. In case you want to remove lines which only consist of whitespace, you could trim them before filtering. Here's an example:

$xml.log.logentry.msg -split "`n" | Foreach { $_.Trim() } | Where { $_ }

As a side note, you have a minor error in your sample xml. The first msg element is never closed.

Here's a full sample using your sample xml and filtering using the script above:

[xml]$xml = @"
<log>
  <logentry revision="152">
    <author>me</author>
    <date>2014-03-28T14:54:27.443978Z</date>
    <msg>Summary 1

* Note 1
* Note 2</msg>
  </logentry>
  <logentry revision="153">
    <author>me</author>
    <date>2014-03-28T16:24:43.438847Z</date>
    <msg>Summary 2</msg>
  </logentry>
  <logentry revision="154">
    <author>me</author>
    <date>2014-03-31T16:00:01.590373Z</date>
    <msg>Summary 3</msg>
  </logentry>
  <logentry revision="155">
    <author>me</author>
    <date>2014-04-01T09:28:09.744015Z</date>
    <msg>Summary 4

* Note 3
* Note 4
    </msg>
  </logentry>
</log>
"@

$xml.log.logentry.msg -split "`n" | Foreach { $_.Trim() } | Where { $_ }
Summary 1
* Note 1
* Note 2
Summary 2
Summary 3
Summary 4
* Note 3
* Note 4

The XML error must be a typo as I've copied it out of a console window. So your command gives me an array of strings, one per line, I guess. How can I convert it into a string with consistent newline characters to write it back to the file, together with the previous content?

You want it added back into the XML and then saved? When you say "consistent newline characters", do you expect both carriage return and line feed or just the line feed character or do your not care if it's either or a mix, as long as they are on the right places?

No, I just use XML as source, but write everything in a plain text file. SVN can also give me plain text, but XML should be safer to parse. I prefer CRLF as that's what Windows uses, and SVN seems to return LF only. Right now I have some ugly mix of both, and missing line breaks between "msg" items. But it should be easy to clean existing line breaks with some Replace() calls.

Just save the result of the previous filtering into a variable called $logtext and then do [string]::join("``r``n", $logtext).Replace("``r``r", "``r") | Out-File c:\myoutput.txt -Encoding UTF8 (just make the double backticks in the code a single backtick, I'm just having issues with stackoverflows handling of control characters), given that you want to save it with UTF8 encoding. That should ensure that there is both carriage return and line feed characters between each line and if there are mixed line feeds in the input it should remove the extra carriage return characters.

powershell - Get multiple text elements from XML - Stack Overflow

xml powershell
Rectangle 27 0

Assuming you have a string array with each element being the name of a variable element you want to remove, you could do the following:

$names = 'ctrl_btn','ctrl_snpt_calctrl'

$xml.variables.variable | ? { $names -contains $_.name } | % {$xml.variables.RemoveChild($_)}

I am able to use Powershell to pull out certain elements within the document (such as grabbing the name or value element based on an index number). However, I don't follow how those indices work (or whatever the indices may be called). For example, look for name[1] gives me ctrl_btn, but I would have assumed that would be name[0]. Could someone explain that?

Xml order is not guaranteed, so don't rely on this. On my machine, for example, I get the results you expect:

$xml.variables.variable.name[0] = ctrl_btn

$xml.variables.variable.name[1] = ctrl_snpt_calctrl

How would I get that to work in a foreach loop that's cycling through a list of hundreds?

This works perfectly! I also learned that to save it, I just have to use the following code: $xml.Save("filepath"). Thanks!

I found a new problem. When a <name></name> node is longer than 76 characters, in the output text file the name is truncated and then three periods are added after it (e.g. example_name_rea...). With the code above, the script won't remove it from the XML document. I added a part to the script to remove the ..., but it still won't remove. I tried changing -contains to -like but that didn't work either. Any suggestions?

Remove a set of XML elements with Powershell - Stack Overflow

xml powershell delete nodes
Rectangle 27 0

In my most recent project I wrote a PowerShell script which loaded the web.config file, modified the necessary XML elements, and saved the file back out again. A bit like this:

param($mode, $src)
$ErrorActionPreference = "stop"
$config = [xml](Get-Content $src)

if ($mode -eq "Production")
{
    $config.SelectSingleNode("/configuration/system.web/compilation").SetAttribute("debug", "false")
    $config.SelectSingleNode("/configuration/system.web/customErrors").SetAttribute("mode", "off")
    $config.SelectSingleNode("/configuration/system.net/mailSettings/smtp/network").SetAttribute("host", "live.mail.server")
    $config.SelectSingleNode("/configuration/connectionStrings/add[@name='myConnectionString']").SetAttribute("connectionString", "Server=SQL; Database=Live")
}
elseif ($mode -eq "Testing")
{
    # etc.
}

$config.Save($src)

This script overwrites the input file with the modifications, but it should be easy to modify it to save to a different file if needed. I have a build script that uses web deployment projects to build the web app, outputting the binaries minus the source code to a different folder - then the build script runs this script to rewrite web.config. The result is a folder containing all the files ready to be placed on the production server.

asp.net - web.config - auto generate a release version - Stack Overflo...

asp.net debugging release web-config
Rectangle 27 0

Thanks for that Keith, however i had some other issue as the cscfg file has a namespace.

But this is when the xml is tied up to a namespace, specially in a CSCFG File in Azure Deployments

$xmlDoc = [System.Xml.XmlDocument](Get-Content $configFile);
$ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
$ns.AddNamespace("ns", $xmlDoc.DocumentElement.NamespaceURI)

$node = $xmlDoc.SelectSingleNode("//ns:Certificates", $ns)
$newNode = $xmlDoc.CreateElement("Certificate");
$newNode.SetAttribute('name',"certificate1")
$newNode.SetAttribute('value',"12345678")
$node.AppendChild($newNode)
$xmlDoc.save($configFile)

How do you add an element to an empty XML (CSCFG) node in PowerShell -...

xml powershell azure cscfg
Rectangle 27 0

You can iterate all children by simply referencing them:

$xmldata.rss.channel.item.guid

However, you don't get autocompletion for them. (This only happens coincidentally on 'item' as that is also a function.)

FYI that only works for V3 and higher as V3 added the member enumeration feature that allows you to "dot" a property on objects in a collection i.e. item is a collection. In V2 you have to do this: $xmldata.rss.channel.item | Foreach {$_.guid}.

Powershell - how i read Elements from a XML File - Stack Overflow

xml powershell
Rectangle 27 0

If you use dot notation to navigate an XML file (e.g. $doc.configuration), Powershell tries to be clever about what it returns.

  • If the target element is empty or only contains a single text node, PS will return a String.
  • If the target element contains child nodes other than text nodes, it will return an XmlElement.
  • If multiple target elements exist, it will return an Object[], where each individual array element is again subject to these rules, e.g. it will either be a String or an XmlElement depending on its contents.
  • If the target element does not exist, PS returns $null.
[xml] $doc = Get-Content($filePath)
$child = $doc.CreateElement("newElement")
$doc.DocumentElement.AppendChild($child)

but you could use $doc.SelectNodes() or $doc.SelectSingleNode() to navigate around the XML document and always have a node/node list returned.

I found that if <configuration> tag is empty, $doc.configuration is String, if not empty - XmlElement. I've solved my problem using $doc.SelectSingleNode('configuration') that returns $null or XmlElement.

How to add a child element for XML in PowerShell - Stack Overflow

xml powershell
Rectangle 27 0

You can do this:

$reader.ReadElementString().Replace("`n","`r`n")

How to emit Windows line endings when writing element strings from XML...

xml powershell newline xmlreader
Rectangle 27 0

From quick experimentation it is indeed a clash, in the sense of attempting to modify the iterator while in the loop. That is, you are initially selecting all elements--including those in the destination. One would think that should be harmless but apparently not. To prove this, change your selection of nodes to this:

$files = $xmlDoc.SelectNodes('//dir[@Id="source"]/file')

... wherein you select only those file nodes in the source. (After all, the ones in the destination do not need to be included since they are already where you want them.) With that change, the code executes as expected.

Here is another variation that will work, going back to your original selector. I changed only the three lines marked with octothorps (#############) to grab the nodes then modify the XML in separate steps:

# Reset and try with a foreach loop
$xmlDoc = $xmlDocOriginalValue.Clone()
$files = $xmlDoc.SelectNodes('//file')
$destination = $xmlDoc.SelectSingleNode('//dir[@Id="destination"]')

Write-Host "`nForeach loop"
Write-Host "XML before: $($xmlDoc.OuterXml)"

$i = 0
$list = @()                                    ##############
foreach($file in $files) { $list += $file }    ##############
foreach($item in $list)                        ##############
{ 
    Write-Host ("Iteration "+$i + ", " + $file.OuterXml)
    $destination.AppendChild($item) | Out-Null 
    $i++
}
Write-Host "XML after: $($xmlDoc.OuterXml)"

Aha, that makes it clear. I'm amazed that my query from a previous line is getting reevaluated by the loop, which explains the infinite loop. Not the first time I've seen PowerShell bends the rules of time ;) The collection returned by SelectNodes must be somehow special vs the $list you created, above. If I get curious I'll interrogate it. Thanks!

Powershell, For vs Foreach when moving XML Elements - Stack Overflow

xml powershell
Rectangle 27 0

function Add-XmlFromFile ([String]$Path,
                          [String]$XPath,
                          [System.Xml.XmlElement]$ParentElement) {

    [System.Xml.XmlElement]$ChildElement = `
        (Select-Xml -Path $Path -XPath $XPath).Node

    [System.Xml.XmlElement]$ImportedElement = `
        $ParentElement.OwnerDocument.ImportNode($ChildElement, $true)

    return $ParentElement.AppendChild($ImportedElement)
}

# Creating new XML object.
$Xml = New-Object Xml
$XmlDeclaration = $Xml.CreateXmlDeclaration("1.0", "UTF-8", $null)
$Xml.AppendChild($XmlDeclaration) | Out-Null

# Creating parent element.
$CreateResourceRequest = $Xml.CreateElement("CreateResourceRequest")

# Adding child elements to parent.
Add-XmlFromFile -Path "File A.xml" `
    -XPath "//ResourceResponse/Resource" `
    -ParentElement $CreateResourceRequest | Out-Null
Add-XmlFromFile -Path "File B.xml" `
    -XPath "//ResourcePrototypeResponse/ResourcePrototype" `
    -ParentElement $CreateResourceRequest | Out-Null
Add-XmlFromFile -Path "File C.xml" `
    -XPath "//Resource" `
    -ParentElement $CreateResourceRequest | Out-Null

# Appending parent to XML object.
$Xml.AppendChild($CreateResourceRequest) | Out-Null

# Saving XML object.
$Xml.OuterXml | Out-File -FilePath "File D.xml" -Encoding "UTF8"

Works perfectly! Not only that but I know understand it a bit better. Wish I would have asked sooner... Thanks again.

So close..... I see that $Xml.AppendChild($XmlDeclaration) | Out-Null inserts 3 bytes before '<?xml..' when writing the file. I wrote the file before adding an additional children and it is coming from that operation. I made sure to set the encoding to utf8 as in: OutFile -Encoding utf8 but I still get: <bh:ef><bh:bb><bh:bf>\<?xml version="1.0" encoding="UTF-8"?> Am I not setting something correctly?

Added -Encoding "UTF8" - indeed by default PS uses UCS-2 Little Endian encoding. However, I cannot reproduce the issue (in PS 2.0) neither with UCS-2 nor with UTF-8 - I don't see any additional characters in the file my code generates.

Thanks Alexander. I'm using PS4 (after upgrading to get beyind the PS3 Invoke-RestMethod bug around the Headers). I also added the -Encoding uft8 switch as the payload ended up as Unicode and I needed plain text. It seems to be an artifact of the way files are written. I found some info about the stream encoding that causes this. I confirmed the data in the file with a hex editor as well as a tcpdump. I've decided to just write the payload directly rather than saving it first. (Should've done that all along!) Thanks again for your quick reply!

How to merge xml files with different elements/nodes into a single fil...

xml powershell
Rectangle 27 0

If you want all guid-elements, then you could use xpath like this:

$xmldata.SelectNodes('//guid') | Select-Object -ExpandProperty "#text"

http://dlbmodigital.microsoft.com/webcasts/wmv/23976_Dnl_L.wmv
http://dlbmodigital.microsoft.com/webcasts/wmv/23977_Dnl_L.wmv
http://dlbmodigital.microsoft.com/webcasts/wmv/23978_Dnl_L.wmv

Thank you for the Hint with xpath. I have to read how it works. My Target is, to find all Hyperlinks (Element guid) and download it. With VB i Count the numbers of guid elements and then download with a For Loop (int_i to Count). With Powershell i have to learn, how is the way to Access the guid elements.

Then this was exactly what you want (except downloading it). Add | Foreach-object { #your download code } and you're done. But unless you have conditions to filter out some of the guid elements, then @mtman's dot-sourcing method is cleaner. I added this to give you a different solution which is compatible with all powershell versions.:-)

Powershell - how i read Elements from a XML File - Stack Overflow

xml powershell
Rectangle 27 0

$delnode = @('Title1','Title2')
($xml.Root.Book.ChildNodes | ? {$delnode -eq $_.InnerText}) | % {
    $xml.Root.Book.RemoveChild($_) | Out-Null
}
  • You want to iterate over the ChildNodes of the $xml.Root.Book element.
  • Those child nodes do not have a property named "Name." They are elements, so they have InnerText properties.
  • RemoveChild() returns the element removed. Since you don't want this element showing up in your output, the call to RemoveChild() gets piped to Out-Null.
  • Note the parentheses that surround the ChildNodes and ? calls, before the For-Each (%) call. These are needed because you do not want to call RemoveChild() on a collection you are currently iterating.

James, it worked beautifully. Really appreciate your effort and passion for not just providing the solution but also explaining about key elements of the solution.

powershell - Removing multiple child elements from XML - Stack Overflo...

xml powershell
Rectangle 27 0

  • You need to enumerate the ChildNodes collection of book
  • You are comparing the wrong element property. You need to compare based on the text value not the element name (which returns "Name")
  • You should use -contains to check if the current node is in the array of items to delete

powershell - Removing multiple child elements from XML - Stack Overflo...

xml powershell
Rectangle 27 0

Your sample XML is incomplete and does not match your code. Also, you want to remove nodes, not attributes. Try this:

Get-ChildItem c:\xml\*.xml | % {
  $xml = [xml](Get-Content $_.FullName)

  $xml.SelectNodes("//property/column") | ? {
    $_.name -eq "Type" -and
      $_.length -eq "20" -and
      $_."sql-type" -eq "varchar" -and
      $_."not-null" -eq "true"
  } | % {
    $_.ParentNode.RemoveChildNode($_)
  }

  $xml.Save($_.fullname)
}

Note that you need to run the script with admin permissions if normal users don't have write permission to the files in C:\xml.

So, I'm running the powershell ise .exe as Administrator and am receiving Exception calling "Save" with "1" argument(s): "Access to the path * is denied". Also, "users" have been granted full permission. I had this similar error before and running it as Admin had fixed it, but not this time.

"Access denied" when users have full control over the file sounds like someone (or something) holding an open handle to that file. You can verify that for instance with Systinternals' handle utility.

Thanks Ansgar. I ended up using .NET to resolve the nodes removal. Removed upwards of 2000 <column ..> nodes in 58 files. Also, to be noted, SelectNodes("//*column").

Powershell - Removing Element within many XML files - Stack Overflow

xml powershell
Rectangle 27 0

After providing the error information to @Richard question, I realized it must be something wrong with the XPath. What could be wrong in the simple XPath? I was trying to get the immediate child node. Then tried the classic debugging technique, write output to console, output the InnerXml of the parent node. In the output I noticed the xml output had namespace. So on further search on stackoverflow found out how to resolve xml namespace.

Here is the link for resolving namespace. And the fixed script.

$csproj = [xml] ( Get-Content $fullProjectPath )

    [System.Xml.XmlNamespaceManager] $nsmgr = $xml.NameTable;
    $nsmgr.AddNamespace('msbuild','http://schemas.microsoft.com/developer/msbuild/2003');

    $csproj.Project.ItemGroup | 
        ForEach-Object { $_.Reference } | 
        where { $_.Include -match "^(Some).+"} |  
        ForEach-Object { 
            if( $_.Private -ne $null ) { 
                $_.RemoveChild($_.SelectSingleNode("msbuild:Private", $nsmgr)) 
            }
        }

+1 for working through this and discovering why stating the error rather than just "it doesn't work" is important, even before asking anyone else.

Thanks. I did try to debug before posting. But I guess its difficult to think straight towards the end of the day. And then following morning you find the answer easily.

How do I remove the Private element (copy local) from csproj (xml) usi...

xml powershell-v2.0 csproj
Rectangle 27 0

My first rule when the pipeline gives me trouble, and things aren't working is to stop using the pipeline, refactor code into parts, and use the ISE to debug with breakpoints on intermediate variables to not only debug, but to discover what properties each object has (like #text, or InnerChild, etc).

This does something very similar to what JamesQMurphy gives as answer, but might also lead to code that is more easily understood, changed, and/or debug.

[xml]$xml = (Get-Content D:\test\t1.xml)

Write-Output "`nbefore..."
$xml.Root.Book | ft

$delnode = 'Title1','Title2'
$nodesToDelete = @()
foreach ($title in $delnode) {
    $nodesToDelete += $xml.Root.Book.ChildNodes | ? {$_.InnerText -eq $title}
}

Write-Output "`nto delete..."
$nodesToDelete | ft

Write-Output "`ndeleting..."
$nodesToDelete | % {$xml.Root.Book.RemoveChild($_) | Out-Null}

Write-Output "`nafter..."
$xml.Root.Book | ft

powershell - Removing multiple child elements from XML - Stack Overflo...

xml powershell