Передача xpath из CSV для замены значения в XML на Powershell - PullRequest
0 голосов
/ 14 января 2020

Нужно прочитать список xml элементов (в виде xpaths) и их новых значений из файла csv ->, а затем найти существующий файл xml, чтобы заменить все элементы xml, которые необходимо обновить, с новыми значениями в данных xpath.

Я получил значения для xpath, которые мне нужно изменить в большом xml, анализируя его в режиме онлайн. Может быть / будет любое количество значений для замены за один раз, поэтому я не могу жестко запрограммировать их в 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"

Затем я вызываю fn чтобы заменить этот xpath новым значением, в xml.

я борюсь только за одну строку , где "-" в строке app-args xpath выше быть проанализированным и каким-то образом отрезать строку xpath там, и, следовательно, отключить замену. Я не могу изменить имена узлов в xml - не вариант.

Я могу просматривать структуру xml в powershell при загрузке:

    $xml = [xml](Get-Content "C:\temp\big.xml")

Итак, автозаполнение просматривая подсказку, я вижу, что:

    PS c:\temp> $xml.configuration.services.objects.object.'app-args'.list.value[0]

покажет мне правильное «заменить это значение 1» в xml.

Но я не могу получить xpath Фраза правильная в CSV, чтобы заменить этот узел. Как я могу прочитать этот узел с "-" в имени, из файла? Я пробовал / "app-args" / и множество других escape-символов, но пока без идей. Использовать что-то кроме 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"

Ответы [ 2 ]

1 голос
/ 14 января 2020

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, не уверен, как предложить другой путь.

0 голосов
/ 14 января 2020

Поскольку вы предоставляете пример с пространством имен, у вас будет немного другой способ определения ваших путей.

Ваше XML помечает пространство имен на объектах, поэтому все объекты под объектами считаются частью указанного c пространства имен. Это пространство имен необходимо при поиске, редактировании или удалении узлов.

$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, включая теги *1008*, должны быть помечены.

Если вы заинтересованы в изменении значений, вам необходимо обновить 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"

CODE

$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")

Новый файл имеет обновленные значения.

Пожалуйста, отметьте ответ, если он отвечает на вопрос в вашем сообщении.

...