如何使用 insertAfter() 在 XML 中的特定元素之后插入元素

How to insert element after a specific element in XML with insertAfter()

我的文件 .xml 看起来像这样

<?xml version="1.0" encoding="UTF-8"?>
<layoutMaster xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="..\schema\m.xsd">
  <deviceFamily>
    <deviceFamilyUniqueID>DeviceFamilyWayside_2</deviceFamilyUniqueID>
    <name>DeviceFamilyWayside_2</name>
    <device>SAMSUNG</device>
    <device>SONY</device>
    <deviceFamilyOptions>
      <name>false</name>
    </deviceFamilyOptions>
  </deviceFamily>
  <deviceFamily>
    <deviceFamilyUniqueID>DeviceFamilyWayside_4</deviceFamilyUniqueID>
    <name>DeviceFamilyWayside_4</name>
    <device>IPHONE</device>
    <device>MAC</device>
    <deviceFamilyOptions>
      <name>false</name>
    </deviceFamilyOptions>
  </deviceFamily>
</layoutMaster>

而且我想在 deviceFamily[deviceFamilyUniqueID ='DeviceFamilyWayside_2'] 添加一个新设备在最后一个设备之后:

所以我在 PowerShell 中尝试了以下代码:

$Path = ".\config.xml"
[xml]$xmlFile = Get-Content -Path $Path

$xmlNewElementDevice = $xmlFile.CreateElement("device")
$xmlNewElementDevice.AppendChild($xmlFile.CreateTextNode("ALCATEL"))

$xmlTargetDeviceWayside = $xmlFile.SelectSingleNode('//layoutMaster/deviceFamily[deviceFamilyUniqueID = "DeviceFamilyWayside_2"]')
$xmlTargetDeviceWayside.InsertAfter($xmlNewElementDevice,$xmlTargetDeviceWayside.name)

但是我得到了这个错误:

Unable to convert the argument "1" (value "DeviceFamilyWayside_2") from "InsertAfter" to type "System.Xml.XmlNode":" Unable to convert "DeviceFamilyWayside_2" value from "System.String" to type "System.Xml.XmlNode". "

我想得到的最终结果是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<layoutMaster xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="..\schema\m.xsd">
  <deviceFamily>
    <deviceFamilyUniqueID>DeviceFamilyWayside_2</deviceFamilyUniqueID>
    <name>DeviceFamilyWayside_2</name>
    <device>SAMSUNG</device>
  <device>SONY</device>
    <device>ALCATEL</device>    <--------------- here
    <deviceFamilyOptions>
      <name>false</name>
    </deviceFamilyOptions>
  </deviceFamily>
  <deviceFamily>
    <deviceFamilyUniqueID>DeviceFamilyWayside_4</deviceFamilyUniqueID>
    <name>DeviceFamilyWayside_4</name>
    <device>IPHONE</device>
  <device>MAC</device>
    <deviceFamilyOptions>
      <name>false</name>
    </deviceFamilyOptions>
  </deviceFamily>
</layoutMaster>

节点之前插入新节点<deviceFamilyOptions>:

[xml]$xml = Get-Content -Path $Path

$newNode = $xml.CreateElement("device")
$newNode.InnerText = 'ALCATEL'

$parent = $xml.SelectSingleNode('//layoutMaster/deviceFamily[deviceFamilyUniqueID="DeviceFamilyWayside_2"]')
$ref    = $parent.SelectSingleNode('./deviceFamilyOptions')

$parent.InsertBefore($newNode, $ref) | Out-Null

或者 select 最后一个 <device> 节点作为参考节点,在其后插入新节点:

$parent = $xml.SelectSingleNode('//layoutMaster/deviceFamily[deviceFamilyUniqueID="DeviceFamilyWayside_2"]')
$ref    = $parent.SelectSingleNode('./device[last()]')

$parent.InsertAfter($newNode, $ref) | Out-Null

还要考虑 XSLT (sibling to XPath), the special-purpose language designed to transform XML. PowerShell can run XSLT 1.0 scripts with the System.Xml.Xsl.XslCompiledTransform, 扩展名。

XSLT (另存为.xsl,一个特殊的.xml文件)

<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 Update Needed Node -->
  <xsl:template match="deviceFamily[deviceFamilyUniqueID = 'DeviceFamilyWayside_2']">    
     <xsl:copy>        
       <xsl:apply-templates select="deviceFamilyUniqueID|name|device"/>          
       <device>ALCATEL</device>
       <xsl:apply-templates select="deviceFamilyOptions"/>
     </xsl:copy>    
  </xsl:template>

</xsl:transform>

Powershell (任何 XML 源和 XSLT 脚本的通用脚本)

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

命令行

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"?>
<layoutMaster xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:noNamespaceSchemaLocation="..\schema\m.xsd">
   <deviceFamily>
      <deviceFamilyUniqueID>DeviceFamilyWayside_2</deviceFamilyUniqueID>
      <name>DeviceFamilyWayside_2</name>
      <device>SAMSUNG</device>
      <device>SONY</device>
      <device>ALCATEL</device>
      <deviceFamilyOptions>
         <name>false</name>
      </deviceFamilyOptions>
   </deviceFamily>
   <deviceFamily>
      <deviceFamilyUniqueID>DeviceFamilyWayside_4</deviceFamilyUniqueID>
      <name>DeviceFamilyWayside_4</name>
      <device>IPHONE</device>
      <device>MAC</device>
      <deviceFamilyOptions>
         <name>false</name>
      </deviceFamilyOptions>
   </deviceFamily>
</layoutMaster>

XSL Transform Demo