Как использовать InsertAfter с PowerShell - PullRequest
4 голосов
/ 17 июня 2011

У меня есть несколько XML-файлов, в которые я хочу вставить содержимое одного XML-файла в другой.Я решил использовать LastChild и метод InsertAfter для достижения этой цели.Пока это не работает для меня.

Вот файл parent.xml:

<manifest>
  <manifestExecution>
    <assetDetail>
      <fileAsset fileAssetGuid="parentguid1">
    <parentfile1 />
      </fileAsset>
      <fileAsset fileAssetGuid="parentguid2">
    <parentfile2 />
      </fileAsset>
    </assetDetail>
  </manifestExecution>
</manifest>

А вот файл child.xml:

<manifest>
  <manifestExecution>
    <assetDetail>
     <fileAsset fileAssetGuid="childguid1">
    <childfile1 />
     </fileAsset>
    </assetDetail>
  </manifestExecution>
</manifest>

ЧтоЯ хочу сделать, это выбрать fileAsset узел (ы) из child.xml и вставить в parent.xml после последнего fileAsset узел в parent.xml.

Вот мой тестовый код:

$parent = [xml] (Get-Content d:\temp\parent.xml)
$parentnode = $parent.manifest.manifestExecution.assetDetail
$child = [xml] (Get-Content d:\temp\child.xml)
$childnode = $child.manifest.manifestExecution.assetDetail.InnerXml
$parentnode.InsertAfter($childnode, $parentnode.LastChild)

Вот сообщение об ошибке, которое я получаю:

Cannot convert argument "0", with value: "<fileAsset fileAssetGuid="childguid1"> <childfile1 /></fileAsset>", for "InsertAfter" to type "System.Xml.XmlNode": "Cannot conver t the "<fileAsset fileAssetGuid="childguid1"><childfile1 /></fileAsset>" value of type "System.String" to type "System.Xml.XmlNode"." At line:5 char:24 + $parentnode.InsertAfter <<<< ($childnode, $parentnode.LastChild) + CategoryInfo : NotSpecified: (:) [], MethodException + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Что я делаю не так?

Ответы [ 2 ]

6 голосов
/ 17 июня 2011

Вам нужно перебрать дочерние элементы $childnode, удалить их из родительского элемента и импортировать их в новый контекст документа ($child и $parent - это разные XmlDocument экземпляры) перед добавлением к $parentnode .

Это добавит все fileAsset узлы из $childnode в $parentnode.

$parent = [xml](get-content d:\temp\parent.xml)
$parentnode = $parent.manifest.manifestexecution.assetdetail
$child = [xml](get-content d:\temp\child.xml)
$childnode = $child.manifest.manifestexecution.assetdetail

while ($childnode.haschildnodes) {
  $cn = $childnode.firstchild
  $cn = $childnode.removechild($cn)
  $cn = $parentnode.ownerdocument.importnode($cn, $true)
  $parentnode.appendchild($cn)
}

К счастью, большинство из этих методов возвращают один и тот же XmlNode или новую его версию, поэтому тело цикла while можно связать вместе следующим образом:

$parentnode.appendchild( $parentnode.ownerdocument.importnode( $childnode.removechild( $childnode.firstchild ), $true ))

InsertAfter(newChild,referenceChild) также может работать, но будет немного по-другому, поскольку для него также требуется ссылка на узел, после которого он будет вставлен.

0 голосов
/ 17 июня 2011

Ваша первая проблема в том, что вы получаете не элемент XML, а строку.Вам нужно получить узел XML из вашего документа XML, но используемый сокращенный метод предполагает, что вам нужна строка.Обычно вы можете принудительно вызвать его, явным образом приведя к [System.Xml.XmlElement], но это не всегда работает.Вы можете надежно получить элемент, используя «SelectSingleNode».

Вы еще не достигли своей второй проблемы, но она не за горами.Получив XML, он все равно не будет работать, потому что он из другого XML-документа, поэтому вам необходимо «импортировать» узел.Вы можете настроить это так, чтобы XML выровнялся так, как вы предполагаете, но код работает.

$parentString = @"
<manifest>
  <manifestExecution>
    <assetDetail>
      <fileAsset fileAssetGuid="parentguid1">
    <parentfile1 />
      </fileAsset>
      <fileAsset fileAssetGuid="parentguid2">
    <parentfile2 />
      </fileAsset>
    </assetDetail>
  </manifestExecution>
</manifest>
"@
$childString = @"
<manifest>
  <manifestExecution>
    <assetDetail>
     <fileAsset fileAssetGuid="childguid1">
    <childfile1 />
     </fileAsset>
    </assetDetail>
  </manifestExecution>
</manifest>
"@

$parent = [xml] ($parentString)
$parentnode = $parent.manifest.manifestExecution.assetDetail
$child = [xml] ($childString)
$xpath = '/manifest/manifestExecution/assetDetail'
$childnode = $child.SelectSingleNode($xpath)
Write-Host("So the child is $($childnode.OuterXML)")
$importedNode = $parent.ImportNode($childNode,$true)
Write-Host("And after importing: $($importedNode.OuterXML)")
$parentnode.InsertAfter($importednode, $parentnode.LastChild)
Write-Host("To finally yield: $($parent.OuterXML)")

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

$childnode = [System.Xml.XmlElement]$child.manifest.manifestExecution.assetDetail.InnerXml
...