InnerXml替换,但只能替换一次

时间:2018-07-25 07:08:47

标签: xml powershell replace

我有两个XML文件,一个具有默认名称和值(名为Test.xml),另一个具有默认名称(名为document.xml)。目标是用值替换默认名称-但仅在首次出现时使用。

这里是Test.xml

<XML-TEST>
    <MyText>Dies ist ein Test</MyText>
    <MyTexttwo>Dies ist noch ein Test</MyTexttwo>
</XML-TEST>

这里是document.xml(末尾很多):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"
    xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex"
    xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:o="urn:schemas-microsoft-com:office:office"
    xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
    xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
    xmlns:v="urn:schemas-microsoft-com:vml"
    xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
    xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
    xmlns:w10="urn:schemas-microsoft-com:office:word"
    xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
    xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
    xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"
    xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex"
    xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"
    xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk"
    xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"
    xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"
    mc:Ignorable="w14 w15 w16se wp14">
  <w:body>
    <w:p w:rsidR="00E64ECE" w:rsidRDefault="00E64ECE" w:rsidP="00E64ECE">
      <w:proofErr w:type="spellStart" />
      <w:r>
        <w:t>MyText</w:t>
      </w:r>
      <w:proofErr w:type="spellEnd" />
    </w:p>
    <w:p w:rsidR="00D50239" w:rsidRPr="00E64ECE" w:rsidRDefault="00E64ECE" w:rsidP="00E64ECE">
      <w:r>
        <w:t>MyTexttwo</w:t>
      </w:r>
      <w:bookmarkStart w:id="0" w:name="_GoBack" />
      <w:bookmarkEnd w:id="0" />
    </w:p>
    <w:sectPr w:rsidR="00D50239" w:rsidRPr="00E64ECE">
      <w:pgSz w:w="11906" w:h="16838" />
      <w:pgMar w:top="1417" w:right="1417" w:bottom="1134" w:left="1417" w:header="708" w:footer="708" w:gutter="0" />
      <w:cols w:space="708" />
      <w:docGrid w:linePitch="360" />
    </w:sectPr>
  </w:body>
</w:document>

我在使用PowerShell做些什么?

  1. 我将Test.xml(带有值的那个)保存在哈希表中:

    PS> $XMLSourceHashtable
    
    Name         Value                                                                                                                                                                                                                                                                                                                                                             
    ----         -----                                                                                                                                                                                                                                                                                                                                                             
    MyText       Dies ist ein Test                                                                                                                                                                                                                                                                                                                                                 
    MyTexttwo    Dies ist noch ein Test
    
  2. document.xml保存到变量$DocumentXml中。

  3. 使用foreach替换我需要的内容:

    foreach ($key in ($XMLSourceHashtable.GetEnumerator())) {
        # If one key.value is "false" replace the 1:1 name with Char
        if ($key | Where-Object {$_.Value -eq "false"}) {
            #$key.Name.Trim()
            #$DocumentXml.InnerXml = $DocumentXml.InnerXml.Replace($key.Name.Trim(), "â˜")
        } elseif ($key | Where-Object {$_.Value -eq "true"}) {
            # If one key.value is "true" replace the 1:1 name with Char
            #$key.Name.Trim()
            #$DocumentXml.InnerXml = $DocumentXml.InnerXml.Replace($key.Name.Trim(), "☒")
        } else {
            # Everything else needs to be replaced by value in hashtable
            #Write-Host $key.Name.Trim() "--------------" $key.Value.Trim()
            #$DocumentXml.InnerXml = $DocumentXml.InnerXml.Replace($key.Name.Trim(), $key.Value.Trim())
        }
    }
    

前两个elseif工作正常,不应考虑它们。我深信else

会发生什么?

当然可以替换文本,但是replace方法将执行以下操作:

document.xml中的值将按以下方式替换:

“ MyText”→“ Dies ist ein Test”
“ MyTexttwo”→Dies ist ein Testtwo“

但是应该是:

“ MyText”→“ Dies ist ein Test”
“ MyTexttwo”→死于测试“

重点是,“ MyTexttwo”中已识别“ MyText”。每个“名称”都是实际唯一的,但不能像唯一一样处理。我知道可以在首次出现时替换,但只能用RegEx替换。但是我无法将xml转换为regex并再次返回。我还能做些什么吗?

2 个答案:

答案 0 :(得分:3)

您的方法太复杂了。使用XPath。原则上-加载,修改,保存:

$document = New-Object xml
$document.Load('Document.xml')

$element = $document.SelectSingleNode("//some/path")
$element.InnerText = "some new value"

$document.Save('Document_2.xml')

这里唯一的复杂之处在于您正在处理Word文档,并且它们使用XML名称空间(在XML源中写为xmlns:foo="...namespace URI..."),因此您也需要使用名称空间(请参阅:{{ 3}}):

$document = New-Object xml
$document.Load('Document.xml')

# use a namespace manager to register the w: namespace prefix
$namespaces = New-Object System.Xml.XmlNamespaceManager $document.NameTable
$namespaces.AddNamespace('w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main')

foreach ($item in $XMLSourceHashtable) {
    $searchText = $item.Name;
    $element = $document.SelectSingleNode("//w:t[.='$searchText']", $namespaces)
    $element.InnerText = $item.Value
}

$document.Save('Document_2.xml')

"//w:t[.='$searchText']"将被插入到//w:t[.='MyText']之类的XPath表达式中,并且此路径将选择输入XML中所有以<w:t>作为其值的'MyText'元素。使用.SelectSingleNode()仅返回其中的第一个,这似乎是您想要的。

您可以使用.SelectNodes()和另一个foreach循环来编辑所有事件:

foreach ($element in $document.SelectNodes("//w:t[.='$searchText']", $namespaces)) {
    $element.InnerText = $item.Value
}

答案 1 :(得分:-1)

尽管Tomalak给予从不的建议是很好的建议,但这是您对问题的回答要点是,“ MyText”在“ MyTexttwo”中被识别。每个“名称”都是实际唯一的,但不能像唯一的那样处理

您使用的Replace方法与WHOLE字符串不匹配。 “ MyTextTwo”以“ MyText”开头,因此在函数中该名称的一部分已替换。然后,“ MyTextTwo”不再存在。

仅当完整字符串匹配且不只是部分匹配时,才进行替换。如果您仍然想使用字符串替换,我建议:

$nameToReplace = $key.Name.Trim()
$DocumentXml.InnerXml = $DocumentXml.InnerXml -replace "\A$nameToReplace\z", $key.Value.Trim()

\A\z符号是锚,用于告诉正则表达式替换字符串必须与您输入的字符串完全相同。 (位置断言)

如果您还需要确保仅在套管也匹配的情况下进行更换,则可以使用

$nameToReplace = $key.Name.Trim()
$DocumentXml.InnerXml = $DocumentXml.InnerXml -creplace "\A$nameToReplace\z", $key.Value.Trim()