Не удается найти способ добавления узла XML с помощью PowerShell - PullRequest
0 голосов
/ 18 января 2020

Я пытаюсь сделать это XML:

<?xml version="1.0" encoding="UTF-8"?>
<nfeProc>
  <NFe>
    <infNFe>
      <det nItem="1">
        <prod>
          <vProd>180.00</vProd>
        </prod>
        <imposto>
          <ICMS>
            <ICMS00>
              <vBC>180.00</vBC>
            </ICMS00>
          </ICMS>
          <PIS>
            <PISNT>04</PISNT>
          </PIS>
    </infNFe>
  </NFe>
</nfeProc>

Похоже:

<?xml version="1.0" encoding="UTF-8"?>
<nfeProc>
  <NFe>
    <infNFe>
      <det nItem="1">
        <prod>
          <vProd>180.00</vProd>
        </prod>
        <imposto>
          <ICMS>
            <ICMS00>
              <vBC>180.00</vBC>
            </ICMS00>
          </ICMS>
          <PIS>
            <PISAliq>0.65</PISAliq>
          </PIS>
    </infNFe>
  </NFe>
</nfeProc>

Не удалось найти простой способ удалить <PISNT>04</PISNT>, поэтому я пошел с это:

   ($produto.imposto.PIS.ChildNodes | Where-Object {$_.Name -eq "PISNT"}) | ForEach-Object {
      [void]$_.ParentNode.RemoveChild($_)
   }

Мне не нравится, но работает. Моя проблема состоит в том, чтобы добавить узел <PISAliq>0.65</PISAliq>, уже перепробовал все, что я нашел и до сих пор не могу работать.

Мой настоящий код:

Get-ChildItem $empresaPath -Filter "*.xml" | ForEach-Object {
   [xml]$xml = Get-Content $_.FullName
   $produtos = $xml.nfeProc.NFe.infNFe.det
   foreach ($produto in $produtos) {
      $cfop = $produto.prod.CFOP
      if ($cfop -eq "5102") {
         $prodVprod = $produto.prod.vProd
         $produto.imposto.ICMS.ICMS00.vBC = $prodVprod
         if ($produto.imposto.PIS.PISNT) {
            ($produto.imposto.PIS.ChildNodes | Where-Object {$_.Name -eq "PISNT"}) | ForEach-Object {
            [void]$_.ParentNode.RemoveChild($_)
         }
         $xmlElt = $produto.imposto.PIS.CreateElement("PISAliq")
         $xmlText = $produto.imposto.PIS.CreateTextNode("0.65")
         $xmlElt.AppendChild($xmlText)
         $xmlElt = $xml.CreateElement("PISAliq")
         $xmlText = $xml.CreateTextNode("0.65")
         $xmlElt.AppendChild($xmlText)
      }
         $xml.Save($_.FullName)
      }
      else {
         Write-Host "$($_.BaseName) CFOP $($cfop) inválido" -ForegroundColor Red
      }
   }
}

Я получаю эту ошибку, используя это код:

+ CategoryInfo          : InvalidOperation: (CreateElement:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound

Почему так сложно добавить один тег в PowerShell? Я ценю любую помощь, заранее спасибо за ваше время.

Ответы [ 2 ]

2 голосов
/ 18 января 2020

Вам необходимо создать узел, используя $xml.CreateElement, затем добавить значение и, наконец, добавить его к нужному узлу.

Пример - На основе вашего образца

#$PISNode = Select-Xml -Xml $xml -XPath '//det/PIS'
$PISNode = $xml.nfeProc.NFe.infNFe.det.PIS
$NewPISALIQNode = $xml.CreateElement("PISAliq")
$NewPISALIQNode.InnerText = 0.65
$PISNode.AppendChild($NewPISALIQNode)

Если вам нужно выбрать конкретные узлы на основе определенных условий c, я рекомендую использовать Select-XML вместе с Xpath для выбора нужных вам c узлов.

XPath может быть немного хитро, так как вам нужно изучить его синта, но в Интернете доступно достаточно ресурсов.

Пример # 2 - Получение указанной c группы узлов PIS

В этом примере получают узлы PIS, имеющие значение ICMS / ICMS500 / vB C, в 180,00

$PISNode = Select-Xml -Xml $xml -XPath "//det[ICMS/ICMS00/vBC[.='180.00']]"
if ($PISNode.count -ge 1){$PISNode = $PISNode.Node.PIS}

Дополнительная заметка

Образец xml в вопросе был неверным на момент написания этой статьи. Поэтому я удалил тег <imposto> и закрыл тег </det>, чтобы получить действительный XML.

Вот фактические XML, с которыми были проверены эти примеры.

<?xml version="1.0" encoding="UTF-8"?>
<nfeProc>
  <NFe>
    <infNFe>
      <det nItem="1">
        <prod>
          <vProd>180.00</vProd>
        </prod>
          <ICMS>
            <ICMS00>
              <vBC>180.00</vBC>
            </ICMS00>
          </ICMS>
          <PIS>
            <PISNT>04</PISNT>
          </PIS>
          </det>
    </infNFe>
  </NFe>
</nfeProc>

Ссылки

MDN - XPath

W3schools - XML и XPath

devhints - таблица Xpath

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

Вы также можете использовать XDocument. XDocument для C#, но вы можете использовать его в Powershell, если добавите несколько методов.

using namespace System.Xml
using namespace System.Xml.Linq
using assembly System.Xml.Linq

function Add-ExMethod ($NamespaceTable) {

    Begin {
        $nsmgr = [XmlNamespaceManager]::new([NameTable]::new());
        $NamespaceTable.GetEnumerator() | foreach { $nsmgr.AddNamespace($_.Name, $_.Value) }
    }
    Process {
        $_ |
        Add-Member ScriptMethod elem  { [XPath.Extensions]::XPathSelectElement($this, $args, $nsmgr)  }.GetNewClosure() -PassThru |
        Add-Member ScriptMethod elems { [XPath.Extensions]::XPathSelectElements($this, $args, $nsmgr) }.GetNewClosure() -PassThru
    }
}


$ns = @{
    n = [XNamespace]"http://www.portalfiscal.inf.br/nfe"
    s = [XNamespace]"http://www.w3.org/2000/09/xmldsig#"
}

Get-ChildItem $empresaPath *.xml | ForEach-Object {

    $doc = [XDocument]::Load($_.FullName) | Add-ExMethod $ns
    $produtos = $doc.elems("//n:det") | Add-ExMethod $ns

    foreach ($p in $produtos) {

       # check CFOP value
        $cfop = $p.elem(".//n:CFOP")
        if ($cfop.Value -ne "5102") { Write-Warning "$_ CFOP $($cfop.Value) inválido"; continue }

        # update vBC value
        $p.elem(".//n:vBC").Value = $p.elem(".//n:vProd").Value

        # replace PISNTwith a new element
        try { $p.elem(".//n:PISNT").ReplaceWith([XElement]::new($ns.n + "PISAliq", "0.65")) } catch {}
    }

    $doc.Save($_.FullName)
}

Без xpath:

Get-ChildItem $empresaPath -Filter *.xml | ForEach-Object {

    $doc = [xml](Get-Content $_.FullName)

    foreach ($p in $doc.nfeProc.NFe.infNFe.det) {

        # check CFOP value
        if ($p.prod.CFOP -ne "5102") { Write-Warning "$_ CFOP $($p.prod.CFOP) invalid"; continue }

        # update vBC value
        $p.imposto.ICMS.ICMS00.vBC = $p.prod.vProd

        # create a new element
        $newElem = $doc.CreateElement("PISAliq", "http://www.portalfiscal.inf.br/nfe")
        $newElem.InnerText = "0.65"

        # replace PISNT with a new element
        $pis = $p.imposto.PIS
        [void]$pis.ReplaceChild($newElem, $pis["PISNT"])
    }

    $doc.Save($_.FullName)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...