修改使用powershell迭代子属性的XML父属性

时间:2017-03-17 23:50:59

标签: xml powershell xml-parsing

所以我有以下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不会为父节点使用而抛出错误但只是不打印任何东西。所有经验丰富的程序员都能为您提供帮助!

2 个答案:

答案 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) }
    }