Reputation: 123
Im writing a PowerShell script for a game that I'm playing to assist with the trading.
I created an XML where I store all the informations I need. The XML looks like:
<stuff>
<resource>
<type Name="Cotton" ID="0000">
<isDGV>1</isDGV>
<Storage>666</Storage>
<AvgBuyingPrice>
</AvgBuyingPrice>
<lastBuyingPrice>42</lastBuyingPrice>
<lowestBuyingPrice>
</lowestBuyingPrice>
<highestBuyingPrice>
</highestBuyingPrice>
<AvgSellingPrice>
</AvgSellingPrice>
<lastSellingPrice>
</lastSellingPrice>
<lowestSellingPrice>
</lowestSellingPrice>
<highestSellingPrice>
</highestSellingPrice>
<BuyingPricesHistory-Last42>
<price>62</price>
<price>42</price>
</BuyingPricesHistory-Last42>
<SellingPricesHistory-Last42>
<price>
</price>
</SellingPricesHistory-Last42>
<TownsProducedIn>
<town>Sampletown</town>
</TownsProducedIn>
<TownsNeededIn>
</TownsNeededIn>
</type>
<type Name="Spices" ID="0001">
<isDGV>0</isDGV>
.
.
</resource>
</stuff>
Now I want to have the possibility to "clean" all the child nodes of a specific resource. So that for example <Storage>666</Storage>
becomes <Storage></Storage>
again.
This is what I thought should work, but apparently, it's not that easy?
(Code is not complete, but $XMLfile
is loaded just fine as it's also used for displaying the values of the resources. the saveXML
function does also work)
$target = $XMLfile.stuff.resource.SelectNodes('type/child::*') | where { $_.Name -eq "$resName" -and $_.ID -eq $ID }
foreach ($Child in $target)
{
$Child.InnerText = ""
}
saveXML $xmlFilePathAndName
Even worse: I don't get any error messages out of that code :-)
Upvotes: 0
Views: 884
Reputation: 107737
Consider an XSLT solution, the special-purpose declarative programming language designed to transform XML documents. In fact, XSLT scripts are well-formed XML files! Like most general purpose languages (C#, Java, Perl, PHP, Python, VB), command line interpreters such as PowerShell and Bash can run XSLT. Of course they can also call external processors (Saxon and Xalan) and aforementioned languages with arguments.
XSLT Script (save as external .xsl file; portable to other languages/platforms)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" method="xml" />
<xsl:strip-space elements="*"/>
<!-- Identity Transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Re-Write for Empty Storage Element -->
<xsl:template match="Storage">
<xsl:copy/>
</xsl:template>
</xsl:transform>
PowerShell Script
param ($xml, $xsl, $output)
if (-not $xml -or -not $xsl -or -not $output) {
Write-Host "& .\xslt.ps1 [-xml] xml-input [-xsl] xsl-input [-output] transform-output"
exit;
}
trap [Exception]{
Write-Host $_.Exception;
}
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform;
$xslt.Load($xsl);
$xslt.Transform($xml, $output);
Write-Host "generated" $output;
Command line
PS > powershell -File PowerShellScript.ps1 Input.xml, XSLTScript.xsl, Output.xml
Upvotes: 0
Reputation: 89325
The problem was, that your SelectNodes()
returns child elements of type
, while the Name
and ID
attributes are at the type
parent element level. So the subsuquent where ...
filter didn't find any element that match the attribute value criteria.
You can use XPath to filter type
element by its attributes, and then return the child elements, all in one expression, like so :
$target = $XMLfile.stuff.resource.SelectNodes("type[@Name='$resName' and @ID='$ID']/*")
Notice that child::
is the default axis in XPath, so when you remove it, leaving only the path separator /
, the engine will implies that you want the default axis to be used here.
Upvotes: 1