2017-03-17 48 views
1

所以我有以下xml(items.xml),我想找到子节点项的属性通过属性迭代,如果我在父节点级别找到相似的属性将其替换为子节点属性,并删除名称以外的子属性。修改通过使用PowerShell迭代Child属性的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不会为父节点使用情况抛出错误,而只是不会打印任何内容。任何有经验的程序员都可以帮助你!

回答

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) } 
    } 
+0

尼斯!谢谢一吨har07! OwnerElement是我寻找的魔法功能,它的功能就像一个魅力! 您是否知道任何其他函数只会在父级节点级别打印属性“名称和值”。 此外,什么是找出在PowerShell中默认的功能最好的办法,遗憾的是似乎没有为PowerShell的是巨大的文档左右。 – Jose

3

因为你的标题提到修改XML,考虑XSLT,仅设计来转换XML文件中的特殊用途的语言。具体而言,您可以运行标识变换(原样复制文档),然后仅使用<xsl:when><xsl:otherwise>条件仅保留与属性名称ancestor::model(祖父母)匹配的属性。 PowerShell可以创建.NET Framework类的对象System.Xml.Xsl.XslCompiledTransform来运行XSLT 1.0脚本。

XSLT(保存为的.xsl到作为参数在PowerShell中传递)

<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> 

的PowerShell(对于任何XML输入和XSLT 1一般脚本。0脚本)

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" 

输出

<?xml version="1.0" encoding="utf-8"?> 
<items> 
    <model type="model1" name="default" price="12.12" date="some_value"> 
    <PriceData> 
     <item name="watch" /> 
    </PriceData> 
    </model> 
    <model type="model2" name="default" price="12.12" date="some_value"> 
    <PriceData> 
     <item name="toy" /> 
    </PriceData> 
    </model> 
    <model type="model3" name="default" price="12.12" date="some_value"> 
    <PriceData> 
     <item name="bread" /> 
    </PriceData> 
    </model> 
</items> 
+0

XSLT是好的,我喜欢它,但其他人似乎因为他们认为它太复杂,学习:( –

+0

同意@AntonKruglov共享相同的态度,我已经回答很多线程XSLT:Python和Java,PHP和Perl中,用VBA褒贬不一的评价。晴OP的沉默下去,就像SQL用于数据库,我总是使用XSLT的XML文件。去另一条路线涉及许多嵌套的循环和逻辑。 – Parfait

+0

谢谢芭菲,你猜它的时候我学习XSLT :) – Jose