2017-03-17 60 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