2014-01-14 9 views
3

Я пытаюсь сделать что-то довольно простое с powershell и xml, но у вас мало проблем. В основном я пытаюсь взять следующий xml ... и отсортировать элементы машины по имени. Затем верните их в XML, чтобы я мог сохранить его обратно в файл.powershell xml сортировать узлы и replacechild

Сортировка, похоже, работает, если выдает новый объект $ new, однако во время заменыchid он жалуется «Невозможно преобразовать аргумент« 0 »со значением:« System.Object [] »для« ReplaceChild », чтобы ввести« System.Xml .XmlNode ":" Невозможно преобразовать «System.Object []» для ввода «System.Xml.XmlNode».

Если я делаю Get-Member как для $ orig, так и для $ new, они оба говорят, что они имеют тип XMLElement, который, как я считаю, наследуется от XMLNode.

Что мне не хватает парней? Сводит меня с ума. Спасибо за вашу помощь!

<company> 
    <stuff> 
    </stuff> 
    <machines> 
     <machine> 
      <name>ca</name> 
      <b>123</b> 
      <c>123</c> 
     </machine> 
     <machine> 
      <name>ad</name> 
      <b>234</b> 
      <c>234</c> 
     </machine> 
     <machine> 
      <name>be</name> 
      <b>345</b> 
      <c>345</c> 
     </machine> 
    </machines> 
    <otherstuff> 
    </otherstuff> 
</company> 

[xml]$xml = (get-content Company.xml) 
[XmlNode]$orig = $xml.Company.Machines 
[XmlNode]$new = ($orig.Machine | sort Name) 
$xml.Company.ReplaceChild($new, $orig) 

ответ

1

Причина этого не работает в том, что $ xml.Company.Machines является одной XmlElement. Чтобы получить набор элементов машины, вам нужно использовать $ xml.Company.Machines.Machine, так что это тот, который вы хотите отсортировать.

Однако метод ReplaceChild не принимает коллекцию, поэтому я предполагаю, что вам придется удалить всех детей, а затем добавить их обратно в том порядке, в котором вы хотите. Что-то вроде следующего должно работать нормально:

[xml]$xml = Get-Content .\Company.xml 
$machines = $xml.company.machines 
$orderedMachineCollection = $machines.machine | Sort Name 
$machines.RemoveAll() 
$orderedMachineCollection | foreach { $machines.AppendChild($_) } | Out-Null 
+0

Да, это именно то, что я закончил делать. Спасибо, что подтвердили причины! – Shaunt

+0

@RobertWesterlund Я реализовал ваше решение, выполнив мой сценарий с помощью PowerShell ISE. Я вижу, как изменяется порядок узлов, но затем я сохраняю изменения в файле, и он не изменяется вообще. – XtianGIS

+0

извините .... глупая ошибка – XtianGIS

2

Здесь есть различные проблемы. Во-первых, ваш sort возвращает список элементов xml, а не один элемент xml. Другая проблема заключается в том, что он возвращает исходные элементы xml, а не копии, поэтому любые манипуляции с DML xml, которые вы используете с ними, также влияют на результат.

Вот простой способ получить то, что вы хотите. Сортируйте в обратном порядке, а затем вставляйте каждый узел поочередно перед другими. Каждый раз, когда вы вставляете узел из результата сортировки он будет автоматически удалить его из исходного набора узлов:

[xml]$xml = @" 
<company> 
    <stuff> 
    </stuff> 
    <machines> 
     <machine> 
      <name>ca</name> 
      <b>123</b> 
      <c>123</c> 
     </machine> 
     <machine> 
      <name>ad</name> 
      <b>234</b> 
      <c>234</c> 
     </machine> 
     <machine> 
      <name>be</name> 
      <b>345</b> 
      <c>345</c> 
     </machine> 
    </machines> 
    <otherstuff> 
    </otherstuff> 
</company> 
"@ 
[System.Xml.XmlNode]$orig = $xml.Company.Machines 
$orig.Machine | sort Name -Descending | 
    foreach { [void]$xml.company.machines.PrependChild($_) } 
$xml.company.machines.machine 

Edit: Трубопровод также может быть написан с восходящим родом (как Дэвид Мартин указал) , и вы можете уменьшить набор текста с помощью узла в переменном:

$orig.Machine | sort Name | % { [void]$orig.AppendChild($_) } 
+0

выглядит очень хорошо - мне пришлось бы удалитьAllAll, прежде чем добавлять их обратно в противном случае я бы в два раза больше правильных? – Shaunt

+0

Нет, это была моя точка зрения о том, что они являются исходными узлами, а не копиями: когда вы вставляете узел обратно в дерево, он перемещает его из своего исходного местоположения. (Код, который я опубликовал, является полным образцом, вы должны иметь возможность копировать/вставлять его и видеть эффект). – Duncan

+0

gotcha. Благодаря! – Shaunt

2

в дополнении к уже отличным ответам Дункан и robert.westerlund Я хотел бы привести пример того, как сделать сортировку XML элементов, если узлы не всегда находятся на одном и том же пути:

[xml]$xml = @" 
<config> 
    <namespace name='Some namespace'> 
     <settings> 
      <setting name='c setting' /> 
      <setting name='a setting' /> 
      <setting name='b setting' /> 
     </settings> 
     <namespace name='Child namespace'> 
      <settings> 
       <setting name='z setting' /> 
       <setting name='y setting' /> 
       <setting name='x setting' /> 
      </settings> 
     </namespace> 
    </namespace> 
</config> 
"@ 

#[xml]$xml = Get-Content "PATH_TO_SOME_FILE.xml" 

$xml.SelectNodes("//namespace/settings") | % { 
    $parentNode = $_; $_.Setting | Sort-Object -Property name | % { 
     $parentNode.AppendChild($_) | Out-Null 
    } 
} 

$xml.Save("PATH_TO_SOME_FILE.xml") 

В этом примере узел settings отображается на разных уровнях в файле xml, и сортировка выполняется относительно этого уровня.

Смежные вопросы