从 CSV 传递 xpath 以用 Powershell 替换 XML 中的值
Passing xpath from CSV to replace value in XML with Powershell
希望从 csv 文件中读取 xml 元素列表(作为 xpath)及其新值 -> 然后,查看现有的 xml 文件以替换任何 xml 元素将被更新,新值在给定的 xpaths 上。
我通过在线解析得到了我需要在大 xml 中更改的 xpath 的值。 can/will 一次可以替换任意数量的值,所以我不能将这些值硬编码到 powershell 中——它需要读取每次所需的每组更改——来自文件(csv) ,我建议。
替换值的一个 xpath 目标是:
/configuration/services/objects/object/app-args/list/value[0]/text()
所以我的 csv 行是:
"/configuration/services/objects/object/app-args/list/value[0]/text()","new value"
然后我在 xml.
中调用 fn 将此 xpath 替换为新值
我正在努力 就在那一行 上面的 app-args xpath 字符串中的“-”正在被解析并以某种方式将 xpath 字符串剪短,并且因此跳出更新。我无法更改 xml 中的节点名称 - 不是一个选项。
加载时可以在powershell中浏览xml结构:
$xml = [xml](Get-Content "C:\temp\big.xml")
所以在提示时自动完成浏览,然后我可以看到:
PS c:\temp> $xml.configuration.services.objects.object.'app-args'.list.value[0]
会在 xml 中显示正确的 "replace this value 1"。
但我无法在 csv 中使用正确的 xpath 措辞来替换此节点。如何从文件中读取名称中带有“-”的节点?我试过 /"app-args"/ 和许多其他转义字符,但暂时没有想法。使用 csv 以外的东西?
之前没有使用过 xpath 的经验,这真的让我非常难受!
参考:(简化)xml 问题区域是:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<spring xmlns="http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.net ../../../../lib/spring.net/spring-objects-1.1.xsd">
<context>
<resource uri="config://spring/objects" />
<context name="services">
<resource uri="config://spring/services/objects" />
</context>
</context>
<objects xmlns="http://www.springframework.net" default-autowire="constructor">
<object name="SomeServicefactory" type="ServiceFactory" />
</objects>
<services>
<objects xmlns="http://www.springframework.net" default-lazy-init="true">
<object name="SystemConfiguration" type="SystemConfiguration">
<app-args name="ConfigCodes">
<list element-type="string">
<value>old value</value>
<!-- Automate me -->
<!-- use case, assume default value as per above, allow the powershell module to override this -->
<value>old value</value>
<!-- Automate me -->
<!-- use case, assume default value as per above, allow the powershell module to override this -->
</list>
</app-args>
</object>
</objects>
</services>
</spring>
</configuration>
我使用的最简单的 csv 是(xpath,它的新值):
"/configuration/services/objects/object/app-args/list/value[0]/text()","new value 1"
"/configuration/services/objects/object/app-args/list/value[1]/text()","new value 2"
XPath 是用于查找节点的路径,使用 SelectNodes
(获取与路径匹配的多个节点)或 SelectSingleNode
获取第一个匹配项。当您更新检索到的节点时,您也会更新父 $xml,因为节点是对父文档的一部分的引用。
用法
$xpath = "/configuration/services/objects/object/app-args/list/value[0]/text()"
$node = $xml.SelectSingleNode($XPath)
$node.Value = "newValue"
$xml.Save("PathToSave")
如果您在某个地方有多个节点,您可以使用 SelectNodes
获取节点列表,然后迭代这些节点以更新值。
$nodes = $xml.SelectNodes("//configuration/services/objects/object/app-args/list/value")
foreach($node in $nodes) {
// Do something with $node.attribute $node.something
}
没有看到您 xml 的实际布局,不确定如何提出其他建议。
由于您提供了一个具有命名空间的示例,因此定义路径的方式会有所不同。
您的 XML 在对象上标记名称空间,因此对象下的所有内容都被视为特定名称空间的一部分。查找、编辑或删除节点时需要此命名空间。
$xml.SelectSingleNode("/configuration/services/x:objects/x:object/x:app-args/x:list/x:value[1]", $Namespace)
$Namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xml.NameTable
$Namespace.AddNamespace("x", "http://www.springframework.net")
由于 /configuration/services
在该命名空间之外,您不必使用 namespaceManager
标记它们。但是 objects
下的所有内容(包括 objects
)都必须进行标记。
如果您有兴趣更改值,则需要更新元素的 InnerText
。
$xml.DocumentElement.SelectSingleNode("/configuration/services/x:objects/x:object/x:app-args/x:list/x:value[1]", $Namespace).InnerText = "New Value for First"
# or you can use // to search for the first element
$xml.DocumentElement.SelectSingleNode("//x:objects/x:object/x:app-args/x:list/x:value[1]", $Namespace).InnerText = "New Value for First"
另请注意,值的索引从 1 开始。没有 value[0]
。因此,您的 csv 需要更新以包含命名空间标签。
"//x:objects/x:object/x:app-args/x:list/x:value[1]","new value 1"
"//x:objects/x:object/x:app-args/x:list/x:value[2]","new value 2"
完整代码
$Namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xml.NameTable
$Namespace.AddNamespace("x", "http://www.springframework.net")
$csv = Get-Content C:\temp\csv.txt
[xml]$xml = Get-Content C:\temp\xml.txt
foreach($line in $csv) {
$items = $line.Replace("""", "").Split(",") # Remove extra quotes
$xml.DocumentElement.SelectSingleNode($items[0], $Namespace).InnerText = $Items[1]
}
$xml.Save("C:\temp\new.txt")
new.txt内容:
<configuration>
<services>
<objects xmlns="http://www.springframework.net" default-lazy-init="true">
<object name="SystemConfiguration" type="SystemConfiguration">
<app-args name="Codes">
<list element-type="string">
<value>new value 1</value>
<value>new value 2</value>
</list>
</app-args>
</object>
</objects>
</services>
</configuration>
---------------------------------------- --------------
更新问题的建议解决方案
根据您的新 xml,我建议使用 Select-XML
路线。
CSV
"//ns:objects/ns:object/ns:app-args/ns:list/ns:value[1]","new value 1"
"//ns:objects/ns:object/ns:app-args/ns:list/ns:value[2]","new value 2"
代码
$csv = Get-Content C:\temp\csv.txt
[xml]$xml = Get-Content C:\temp\xml.txt
$ns = @{ns='http://www.springframework.net'}
foreach($line in $csv)
{
$items = $line.Replace("""", "").Split(",") # Remove extra quotes
$nodeRef = Select-Xml -Xml $xml -XPath $items[0] -Namespace $ns
$nodeRef.Node.InnerText = $items[1]
}
$xml.Save("C:\temp\new.txt")
新文件更新了值。
如果 post 回答了您 post 中的问题,请将其标记为已回答。
希望从 csv 文件中读取 xml 元素列表(作为 xpath)及其新值 -> 然后,查看现有的 xml 文件以替换任何 xml 元素将被更新,新值在给定的 xpaths 上。
我通过在线解析得到了我需要在大 xml 中更改的 xpath 的值。 can/will 一次可以替换任意数量的值,所以我不能将这些值硬编码到 powershell 中——它需要读取每次所需的每组更改——来自文件(csv) ,我建议。
替换值的一个 xpath 目标是:
/configuration/services/objects/object/app-args/list/value[0]/text()
所以我的 csv 行是:
"/configuration/services/objects/object/app-args/list/value[0]/text()","new value"
然后我在 xml.
中调用 fn 将此 xpath 替换为新值我正在努力 就在那一行 上面的 app-args xpath 字符串中的“-”正在被解析并以某种方式将 xpath 字符串剪短,并且因此跳出更新。我无法更改 xml 中的节点名称 - 不是一个选项。
加载时可以在powershell中浏览xml结构:
$xml = [xml](Get-Content "C:\temp\big.xml")
所以在提示时自动完成浏览,然后我可以看到:
PS c:\temp> $xml.configuration.services.objects.object.'app-args'.list.value[0]
会在 xml 中显示正确的 "replace this value 1"。
但我无法在 csv 中使用正确的 xpath 措辞来替换此节点。如何从文件中读取名称中带有“-”的节点?我试过 /"app-args"/ 和许多其他转义字符,但暂时没有想法。使用 csv 以外的东西?
之前没有使用过 xpath 的经验,这真的让我非常难受!
参考:(简化)xml 问题区域是:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<spring xmlns="http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.net ../../../../lib/spring.net/spring-objects-1.1.xsd">
<context>
<resource uri="config://spring/objects" />
<context name="services">
<resource uri="config://spring/services/objects" />
</context>
</context>
<objects xmlns="http://www.springframework.net" default-autowire="constructor">
<object name="SomeServicefactory" type="ServiceFactory" />
</objects>
<services>
<objects xmlns="http://www.springframework.net" default-lazy-init="true">
<object name="SystemConfiguration" type="SystemConfiguration">
<app-args name="ConfigCodes">
<list element-type="string">
<value>old value</value>
<!-- Automate me -->
<!-- use case, assume default value as per above, allow the powershell module to override this -->
<value>old value</value>
<!-- Automate me -->
<!-- use case, assume default value as per above, allow the powershell module to override this -->
</list>
</app-args>
</object>
</objects>
</services>
</spring>
</configuration>
我使用的最简单的 csv 是(xpath,它的新值):
"/configuration/services/objects/object/app-args/list/value[0]/text()","new value 1"
"/configuration/services/objects/object/app-args/list/value[1]/text()","new value 2"
XPath 是用于查找节点的路径,使用 SelectNodes
(获取与路径匹配的多个节点)或 SelectSingleNode
获取第一个匹配项。当您更新检索到的节点时,您也会更新父 $xml,因为节点是对父文档的一部分的引用。
用法
$xpath = "/configuration/services/objects/object/app-args/list/value[0]/text()"
$node = $xml.SelectSingleNode($XPath)
$node.Value = "newValue"
$xml.Save("PathToSave")
如果您在某个地方有多个节点,您可以使用 SelectNodes
获取节点列表,然后迭代这些节点以更新值。
$nodes = $xml.SelectNodes("//configuration/services/objects/object/app-args/list/value")
foreach($node in $nodes) {
// Do something with $node.attribute $node.something
}
没有看到您 xml 的实际布局,不确定如何提出其他建议。
由于您提供了一个具有命名空间的示例,因此定义路径的方式会有所不同。
您的 XML 在对象上标记名称空间,因此对象下的所有内容都被视为特定名称空间的一部分。查找、编辑或删除节点时需要此命名空间。
$xml.SelectSingleNode("/configuration/services/x:objects/x:object/x:app-args/x:list/x:value[1]", $Namespace)
$Namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xml.NameTable
$Namespace.AddNamespace("x", "http://www.springframework.net")
由于 /configuration/services
在该命名空间之外,您不必使用 namespaceManager
标记它们。但是 objects
下的所有内容(包括 objects
)都必须进行标记。
如果您有兴趣更改值,则需要更新元素的 InnerText
。
$xml.DocumentElement.SelectSingleNode("/configuration/services/x:objects/x:object/x:app-args/x:list/x:value[1]", $Namespace).InnerText = "New Value for First"
# or you can use // to search for the first element
$xml.DocumentElement.SelectSingleNode("//x:objects/x:object/x:app-args/x:list/x:value[1]", $Namespace).InnerText = "New Value for First"
另请注意,值的索引从 1 开始。没有 value[0]
。因此,您的 csv 需要更新以包含命名空间标签。
"//x:objects/x:object/x:app-args/x:list/x:value[1]","new value 1"
"//x:objects/x:object/x:app-args/x:list/x:value[2]","new value 2"
完整代码
$Namespace = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $xml.NameTable
$Namespace.AddNamespace("x", "http://www.springframework.net")
$csv = Get-Content C:\temp\csv.txt
[xml]$xml = Get-Content C:\temp\xml.txt
foreach($line in $csv) {
$items = $line.Replace("""", "").Split(",") # Remove extra quotes
$xml.DocumentElement.SelectSingleNode($items[0], $Namespace).InnerText = $Items[1]
}
$xml.Save("C:\temp\new.txt")
new.txt内容:
<configuration>
<services>
<objects xmlns="http://www.springframework.net" default-lazy-init="true">
<object name="SystemConfiguration" type="SystemConfiguration">
<app-args name="Codes">
<list element-type="string">
<value>new value 1</value>
<value>new value 2</value>
</list>
</app-args>
</object>
</objects>
</services>
</configuration>
---------------------------------------- --------------
更新问题的建议解决方案
根据您的新 xml,我建议使用 Select-XML
路线。
CSV
"//ns:objects/ns:object/ns:app-args/ns:list/ns:value[1]","new value 1"
"//ns:objects/ns:object/ns:app-args/ns:list/ns:value[2]","new value 2"
代码
$csv = Get-Content C:\temp\csv.txt
[xml]$xml = Get-Content C:\temp\xml.txt
$ns = @{ns='http://www.springframework.net'}
foreach($line in $csv)
{
$items = $line.Replace("""", "").Split(",") # Remove extra quotes
$nodeRef = Select-Xml -Xml $xml -XPath $items[0] -Namespace $ns
$nodeRef.Node.InnerText = $items[1]
}
$xml.Save("C:\temp\new.txt")
新文件更新了值。
如果 post 回答了您 post 中的问题,请将其标记为已回答。