如何使用 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>
我的文件 .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>