使用 Powershell 添加行到 XML 文件
Add Line To XML File With Powershell
提前致歉,因为我对 Powershell 的了解非常基础,对 XML 的了解甚至更少。但是我有一个 XML 文件,我需要向其中添加一行,我正在尝试弄清楚如何使用 Powershell 来完成它(因为最后,我将不得不这样做一些千次)。所以 file.xml 格式如下:
<?xml version="1.0" encoding="utf-8"?>
<Sources>
<add id="Source1" type="Test" distance="1">
<parameters>
<name />
<address />
<otherIdentifiers>FirstSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.194</host>
<port>192</port>
</parameters>
</add>
<add id="Source2" type="Prod" distance="2">
<parameters>
<name />
<address />
<otherIdentifiers>SecondSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.195</host>
<port>193</port>
<prod.baseUrl>http://SOURCE2PRODURL/Page</prod.baseUrl>
<cache.enablePrefetch>false</cache.enablePrefetch>
</parameters>
</add>
<add id="Source3" type="Prod" distance="3">
<parameters>
<name />
<address />
<otherIdentifiers>ThirdSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.197</host>
<port>194</port>
<prod.baseUrl>http://SOURCE3PRODURL/Page</prod.baseUrl>
<cache.enablePrefetch>true</cache.enablePrefetch>
</parameters>
</add>
</Sources>
我的目标是找到 type="Prod" 的任何来源,并在 prod.baseURL 行之后添加以下行:
<timeout>500</timeout>
所以最后,Source2 和 Source3 的部分基本上应该格式化为:
<add id="Source2" type="Prod" distance="2">
<parameters>
<name />
<address />
<otherIdentifiers>SecondSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.195</host>
<port>193</port>
<prod.baseUrl>http://SOURCE2PRODURL/Page</prod.baseUrl>
<timeout>500</timeout>
<cache.enablePrefetch>false</cache.enablePrefetch>
</parameters>
</add>
<add id="Source3" type="Prod" distance="3">
<parameters>
<name />
<address />
<otherIdentifiers>ThirdSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.197</host>
<port>194</port>
<prod.baseUrl>http://SOURCE3PRODURL/Page</prod.baseUrl>
<timeout>500</timeout>
<cache.enablePrefetch>true</cache.enablePrefetch>
</parameters>
</add>
这就是我尝试过的方法。如果它完全没有意义,我道歉,因为它对我来说完全没有意义......
$Path = .\File.xml
[XML]$File = Get-Content -Path $Path -Raw
$Sources = $File.Sources.add | Where-Object type -EQ "Prod"
$URL = $Sources.parameters.{prod.baseurl}
$Timeout = [XML]"@
<add name='timeout' value='500'/>
@"
Foreach($Source in $Sources){
$Timeout.InsertAfter($File.ImportNode($Timeout.parameters, $true), $URL)
}
$File.Save($Path)
最后,我得到:
Cannot convert value "@
<add name='timeout' value='500'/>
@" to type "System.Xml.XmlDocument". Error: "The specified node cannot be inserted as the valid child of this node, because the specified node is the
wrong type."
我意识到这很丑陋而且完全错误,但是谁能指出我正确的方向?
问题是,当您使用简单的点表示法访问 XML 元素时,在某些情况下,PowerShell 会将这些元素转换为其他更简单的数据类型。在您的情况下,它将 prod.baseUrl
元素转换为 String
,因为它仅包含字符串内容。
这是一个问题,因为 XmlElement.InsertAfter()
需要 XmlNode
类型的参数,而不是 String
。
解决方案使用 XPath
代替:
# Select child elements using XPath expression
$parameters = $File.SelectNodes("/Sources/add[@type='Prod']/parameters")
# For all found child elements
foreach( $param in $parameters ) {
# Create timeout element
$timeout = $File.CreateElement('timeout')
$timeout.InnerText = '500'
# Insert timeout element after 'prod.baseUrl' element
$param.InsertAfter( $timeout, $param.SelectSingleNode('prod.baseUrl') )
}
考虑 XSLT, the special purpose language designed to transform XML files like conditionally adding nodes in select locations. All this can be done without any looping. Even add templates for other needed conditional modifications. PowerShell can run XSLT 1.0 scripts via the MS XslCompiledTransform API. In fact, this online demo 使用相同的 API 引擎!
XSLT (另存为.xsl文件,一个特殊的.xml文件)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- CONDITIONALLY ADD NODE AFTER prod.baseUrl -->
<xsl:template match="prod.baseUrl">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
<xsl:if test="ancestor::add/@type = 'Prod'">
<timeout>500</timeout>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
PowerShell
$xml = "C:\Path\To\Input.xml";
$xsl = "C:\Path\To\Script.xsl";
$output = "C:\Path\To\Output.xml";
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform;
$settings = New-Object System.Xml.Xsl.XsltSettings($true, $true);
$resolver = New-Object System.Xml.XmlUrlResolver;
$xslt.Load($xsl, $settings, $resolver);
$xslt.Transform($xml, $output);
Write-Host "generated" $output;
输出
<?xml version="1.0" encoding="utf-8"?>
<Sources>
<add id="Source1" type="Test" distance="1">
<parameters>
<name />
<address />
<otherIdentifiers>FirstSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.194</host>
<port>192</port>
</parameters>
</add>
<add id="Source2" type="Prod" distance="2">
<parameters>
<name />
<address />
<otherIdentifiers>SecondSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.195</host>
<port>193</port>
<prod.baseUrl>http://SOURCE2PRODURL/Page</prod.baseUrl>
<timeout>500</timeout>
<cache.enablePrefetch>false</cache.enablePrefetch>
</parameters>
</add>
<add id="Source3" type="Prod" distance="3">
<parameters>
<name />
<address />
<otherIdentifiers>ThirdSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.197</host>
<port>194</port>
<prod.baseUrl>http://SOURCE3PRODURL/Page</prod.baseUrl>
<timeout>500</timeout>
<cache.enablePrefetch>true</cache.enablePrefetch>
</parameters>
</add>
</Sources>
提前致歉,因为我对 Powershell 的了解非常基础,对 XML 的了解甚至更少。但是我有一个 XML 文件,我需要向其中添加一行,我正在尝试弄清楚如何使用 Powershell 来完成它(因为最后,我将不得不这样做一些千次)。所以 file.xml 格式如下:
<?xml version="1.0" encoding="utf-8"?>
<Sources>
<add id="Source1" type="Test" distance="1">
<parameters>
<name />
<address />
<otherIdentifiers>FirstSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.194</host>
<port>192</port>
</parameters>
</add>
<add id="Source2" type="Prod" distance="2">
<parameters>
<name />
<address />
<otherIdentifiers>SecondSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.195</host>
<port>193</port>
<prod.baseUrl>http://SOURCE2PRODURL/Page</prod.baseUrl>
<cache.enablePrefetch>false</cache.enablePrefetch>
</parameters>
</add>
<add id="Source3" type="Prod" distance="3">
<parameters>
<name />
<address />
<otherIdentifiers>ThirdSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.197</host>
<port>194</port>
<prod.baseUrl>http://SOURCE3PRODURL/Page</prod.baseUrl>
<cache.enablePrefetch>true</cache.enablePrefetch>
</parameters>
</add>
</Sources>
我的目标是找到 type="Prod" 的任何来源,并在 prod.baseURL 行之后添加以下行:
<timeout>500</timeout>
所以最后,Source2 和 Source3 的部分基本上应该格式化为:
<add id="Source2" type="Prod" distance="2">
<parameters>
<name />
<address />
<otherIdentifiers>SecondSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.195</host>
<port>193</port>
<prod.baseUrl>http://SOURCE2PRODURL/Page</prod.baseUrl>
<timeout>500</timeout>
<cache.enablePrefetch>false</cache.enablePrefetch>
</parameters>
</add>
<add id="Source3" type="Prod" distance="3">
<parameters>
<name />
<address />
<otherIdentifiers>ThirdSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.197</host>
<port>194</port>
<prod.baseUrl>http://SOURCE3PRODURL/Page</prod.baseUrl>
<timeout>500</timeout>
<cache.enablePrefetch>true</cache.enablePrefetch>
</parameters>
</add>
这就是我尝试过的方法。如果它完全没有意义,我道歉,因为它对我来说完全没有意义......
$Path = .\File.xml
[XML]$File = Get-Content -Path $Path -Raw
$Sources = $File.Sources.add | Where-Object type -EQ "Prod"
$URL = $Sources.parameters.{prod.baseurl}
$Timeout = [XML]"@
<add name='timeout' value='500'/>
@"
Foreach($Source in $Sources){
$Timeout.InsertAfter($File.ImportNode($Timeout.parameters, $true), $URL)
}
$File.Save($Path)
最后,我得到:
Cannot convert value "@
<add name='timeout' value='500'/>
@" to type "System.Xml.XmlDocument". Error: "The specified node cannot be inserted as the valid child of this node, because the specified node is the
wrong type."
我意识到这很丑陋而且完全错误,但是谁能指出我正确的方向?
问题是,当您使用简单的点表示法访问 XML 元素时,在某些情况下,PowerShell 会将这些元素转换为其他更简单的数据类型。在您的情况下,它将 prod.baseUrl
元素转换为 String
,因为它仅包含字符串内容。
这是一个问题,因为 XmlElement.InsertAfter()
需要 XmlNode
类型的参数,而不是 String
。
解决方案使用 XPath
代替:
# Select child elements using XPath expression
$parameters = $File.SelectNodes("/Sources/add[@type='Prod']/parameters")
# For all found child elements
foreach( $param in $parameters ) {
# Create timeout element
$timeout = $File.CreateElement('timeout')
$timeout.InnerText = '500'
# Insert timeout element after 'prod.baseUrl' element
$param.InsertAfter( $timeout, $param.SelectSingleNode('prod.baseUrl') )
}
考虑 XSLT, the special purpose language designed to transform XML files like conditionally adding nodes in select locations. All this can be done without any looping. Even add templates for other needed conditional modifications. PowerShell can run XSLT 1.0 scripts via the MS XslCompiledTransform API. In fact, this online demo 使用相同的 API 引擎!
XSLT (另存为.xsl文件,一个特殊的.xml文件)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- CONDITIONALLY ADD NODE AFTER prod.baseUrl -->
<xsl:template match="prod.baseUrl">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
<xsl:if test="ancestor::add/@type = 'Prod'">
<timeout>500</timeout>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
PowerShell
$xml = "C:\Path\To\Input.xml";
$xsl = "C:\Path\To\Script.xsl";
$output = "C:\Path\To\Output.xml";
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform;
$settings = New-Object System.Xml.Xsl.XsltSettings($true, $true);
$resolver = New-Object System.Xml.XmlUrlResolver;
$xslt.Load($xsl, $settings, $resolver);
$xslt.Transform($xml, $output);
Write-Host "generated" $output;
输出
<?xml version="1.0" encoding="utf-8"?>
<Sources>
<add id="Source1" type="Test" distance="1">
<parameters>
<name />
<address />
<otherIdentifiers>FirstSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.194</host>
<port>192</port>
</parameters>
</add>
<add id="Source2" type="Prod" distance="2">
<parameters>
<name />
<address />
<otherIdentifiers>SecondSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.195</host>
<port>193</port>
<prod.baseUrl>http://SOURCE2PRODURL/Page</prod.baseUrl>
<timeout>500</timeout>
<cache.enablePrefetch>false</cache.enablePrefetch>
</parameters>
</add>
<add id="Source3" type="Prod" distance="3">
<parameters>
<name />
<address />
<otherIdentifiers>ThirdSource</otherIdentifiers>
<supportedQueryTypes>Document</supportedQueryTypes>
<host>10.5.19.197</host>
<port>194</port>
<prod.baseUrl>http://SOURCE3PRODURL/Page</prod.baseUrl>
<timeout>500</timeout>
<cache.enablePrefetch>true</cache.enablePrefetch>
</parameters>
</add>
</Sources>