所以我有以下xml(items.xml),我想找到子节点项的属性迭代属性,如果我在父节点级别找到相似的属性,用子节点属性替换相同的属性并删除除了名称之外的子属性。
<items>
<model type="model1" name="default" price="12.12" date="some_value">
<PriceData>
<item name="watch" price="24.28" date="2013-12-01" />
</PriceData>
</model>
<model type="model2" name="default" price="12.12" date="some_value">
<PriceData>
<item name="toy" price="22.34" date="2013-12-02"/>
</PriceData>
</model>
<model type="model3" name="default" price="12.12" date="some_value">
<PriceData>
<item name="bread" price="24.12" date="2013-12-03"/>
</PriceData>
</model>
</items>
最终的xml应该如下所示
<items>
<model type="model1" name="watch" price="24.28" date="2013-12-0">
<PriceData>
<item name="watch" />
</PriceData>
</model>
<model type="model2" name="toy" price="22.34" date="2013-12-02">
<PriceData>
<item name="toy" "/>
</PriceData>
</model>
<model type="model3" name="bread" price="24.12" date="2013-12-03">
<PriceData>
<item name="bread" />
</PriceData>
</model>
</items>
我能够在子级别获取属性,但我无法从子级别遍历回父节点。
以下是我尝试访问父节点的代码
[xml]$model = get-content items.xml
$model.SelectNodes("//item/@*")
Output:
#text
-----
watch
24.28
2013-12-01
toy
22.34
2013-12-02
bread
24.12
2013-12-03
$model.SelectNodes("//item/@*") | foreach {write-host $_.parentnode}
No Output:
$model.SelectNodes("//item/@*") | foreach {write-host $_.parentnode.parentnode}
No Output:
我可以按如下方式获取子节点的属性名称:
$model.SelectNodes("//item/@*") | foreach {write-host $_.name}
Output:
PS C:\BIOS_Work_Dir\Kit_Manifest_test> $model.SelectNodes("//item/@*") | foreach {write-host $_.name}
name
price
date
name
price
date
name
price
date
现在对于每个属性,我只需要返回父节点,检查是否存在类似属性并将其替换为子节点属性
所以,我正在寻找像
这样的东西$model.SelectNodes("//item/@*") | foreach {($_.name).parentnode.parentnode.($_.name)} | <some code to replace parentnode attribute with child attribute>
然后删除像
这样的子属性$model.SelectNodes("//item/@*") | where {$_.name -notlike "name"} | foreach {$_.Removeattribute()}
如果这两个都可以在一个令人敬畏的单一命令中完成
也许我也试图在一行中做很多事情
任何指针都非常感谢! 不确定我在这里做错了什么因为powershell不会为父节点使用而抛出错误但只是不打印任何东西。所有经验丰富的程序员都能为您提供帮助!
答案 0 :(得分:3)
由于您的标题提到修改XML ,请考虑XSLT,这是专门用于转换XML文件的专用语言。具体来说,您可以运行 Identity Transform (按原样复制文档),然后使用ancestor::model
和{{仅保留与<xsl:when>
(祖父母)属性名称匹配的属性1}}条件。 PowerShell可以创建.NET Framework类System.Xml.Xsl.XslCompiledTransform的对象,以运行XSLT 1.0脚本。
XSLT (另存为.xsl将作为PowerShell中的参数传递)
<xsl:otherwise>
PowerShell (任何XML输入和XSLT 1.0脚本的通用脚本)
<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>
<!-- Conditionally Keep Item Attributes -->
<xsl:template match="item/@*[name()!='name']">
<xsl:variable name="item_attr" select="name()"/>
<xsl:choose>
<xsl:when test="ancestor::model/@*[name()=$item_attr]"/>
<xsl:otherwise><xsl:copy/></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:transform>
命令行致电
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;
Read-Host -Prompt "Press Enter to exit";
<强>输出强>
Powershell.exe -File "C:\Path\To\PowerShell\Script.ps1"^
"C:\Path\To\Input.xml" "C:\Path\To\XSLTScript.xsl" "C:\Path\To\Ouput.xml"
答案 1 :(得分:1)
您可以通过OwnerElement
属性从属性中获取父元素。所以这是获得所需输出的一种可能方式:
$model.SelectNodes("//item/@*") |
ForEach {
# set attributes value on `model` element
$_.OwnerElement.ParentNode.ParentNode.SetAttribute($_.LocalName, $_.Value)
# remove attributes except `name` from `item` element
If ($_.LocalName -ne "name") { $_.OwnerElement.RemoveAttribute($_.LocalName) }
}