【问题标题】:How do I create a subset of an xml file or filter out child nodes of an xml file using powershell?如何使用 powershell 创建 xml 文件的子集或过滤掉 xml 文件的子节点?
【发布时间】:2015-04-28 17:11:43
【问题描述】:

我有一个像下面这样的 xml 文件

...
<body>
  <node1 attribute1="attr1">
     <child1 attribute1="A">
       <grandchild>
       </grandchild>
     </child1>
     <child2 attribute1="B">
       <grandchild>
       </grandchild>
     </child2>
  </node1>
  <node2 attribute1="attr1">
     <child1 attribute1="A">
       <grandchild>
       </grandchild>
     </child1>
     <child2 attribute1="B">
       <grandchild>
       </grandchild>
     </child2>
  </node2>
</body>   

我想使用 powershell 创建另一个 xml 文件,其中仅包含所有 child1 节点或仅包含所有 child2 节点。

我也读过一些关于 xslt 转换的文章,但也不熟悉。

感谢您的帮助。

【问题讨论】:

    标签: xml powershell xslt


    【解决方案1】:

    这是一个替代解决方案,不使用 xpath,而是使用字符串替换和正则表达式。效率高,可以写成一行(看最后一行)。

    我的文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <body>
      <node1 attribute1="attr1">
         <child1 attribute1="A">
           <grandchild>
           </grandchild>
         </child1>
         <child2 attribute1="B">
           <grandchild>
           </grandchild>
         </child2>
      </node1>
      <node2 attribute1="attr1">
         <child1 attribute1="A">
           <grandchild>
           </grandchild>
         </child1>
         <child2 attribute1="B">
           <grandchild>
           </grandchild>
         </child2>
      </node2>
    </body> 
    

    我首先使用 Get-Content-raw 参数将其作为单个字符串加载到 var ($a) 中。

    $a =Get-Content 'D:\temp\M4.xml' -raw
    

    然后我用一个正则表达式来替换你不想要的节点。

    $a -replace '(?sm)     <child1.*?Child1>\r\n','' | set-content 'd:\temp\filewithoutchild1.xml'
    

    结果是:

    <?xml version="1.0" encoding="utf-8"?>
    <body>
      <node1 attribute1="attr1">
         <child2 attribute1="B">
           <grandchild>
           </grandchild>
         </child2>
      </node1>
      <node2 attribute1="attr1">
         <child2 attribute1="B">
           <grandchild>
           </grandchild>
         </child2>
      </node2>
    </body> 
    

    正则表达式的诀窍是使用 (?sm) 你会发现一个很好的解释here

    使用一行:

    (Get-Content 'D:\temp\M4.xml' -raw) -replace '(?sm)     <child1.*?Child1>\r\n','' | set-content 'd:\temp\filewithoutchild1.xml'
    

    于 2015 年 5 月 5 日编辑

    所以现在工作文件看起来像:

    <?xml version="1.0" encoding="utf-8"?>
    <body>
      <node1 attribute1="attr1">
         <child1 attribute1="A">
           <grandchild>
           </grandchild>
         </child1>
         <child1 attribute1="B">
           <grandchild>
           </grandchild>
         </child1>
         <child1 attribute1="C">
           <grandchild>
           </grandchild>
         </child1>   
      </node1>
      <node2 attribute1="attr1">
         <child1 attribute1="A">
           <grandchild>
           </grandchild>
         </child1>
         <child1 attribute1="B">
           <grandchild>
           </grandchild>
         </child1>
         <child1 attribute1="C">
           <grandchild>
           </grandchild>
         </child1>   
      </node2>
    </body> 
    

    这是允许您仅选择所需子节点的代码。虽然它在技术上有效,但我并不为此感到骄傲。

    我使用相同的方式,但这次我循环删除标签,而它只存在您需要的标签。在示例中,我保留“C”国家代码。

    Clear-Host
    
    $a =Get-Content 'D:\temp\M.xml' -raw 
    $reg = [regex]'(?sm)(  <child1.*?</child1>)'
    $tagMatches = $reg.Matches($a)
    
    $blRemoved = $true
    while ($blRemoved)
    {
      $tagMatches = $reg.Matches($a)
      $blRemoved = $false
      foreach ($tagMatch in $tagMatches)
      {
        if ($tagMatch.value -notlike "*`"C`"*")
        {
          Write-Host $tagMatch.value
          $a = $a.Remove($tagMatch.Index,$tagMatch.Length+4)
          $blRemoved = $true
          break
        }
      }
    }
    
    $a
    

    【讨论】:

    • 感谢您的帮助。你能帮我在这个例子中做相反的事情吗?我的例子是一个糟糕的例子。我应该在示例中添加更多的孩子。所以这个例子应该有很多子元素,我只想要生成的 xml 文件中的一个子元素。
    • 您能否更准确地提供有关您要抑制的标签的更多详细信息?
    • 所以在每个节点中我都有很多子标签,我只想保留其中一个。使用您的解决方案,您可以从 2 个标签中过滤掉您不想要的一个,因此很容易排除一个,但如果我有很多,保留一个会更容易。我没有足够的空间来评论这条评论,将继续下一条。
    • 前。 ... child2> ... ... ...
    • 如果您能够建立一个包含所有要抑制的标签的列表,这可能很简单。你能分享一个原始文件吗? node1 下是否存在所有可能的孩子?这也应该有帮助?
    【解决方案2】:

    PowerShell 有一个名为Select-XML 的内置 Cmdlet,非常适合此任务。假设您已将此 XML 文件存储在名为 .\SomeFile.xml 的文件中,并且想要获取名为 'Child1' 的所有节点

    $xml = [xml](gc T:\SomeFile.xml)
    $xml | Select-Xml -XPath //child1
    
    Node   Path        Pattern 
    ----   ----        ------- 
    child1 InputStream //child1
    child1 InputStream //child1
    

    您使用 XPath 的“//”符号表示“给我所有以 this 结尾的节点”来获取子节点。如果您想做一些更复杂的事情,您需要查看一些 XPath 参考。

    这是一个快速参考指南。请务必查看'Selecting Nodes' here,了解更多 XPath 优点..

    【讨论】:

    • FoxDeploy,感谢您的帮助。我从您的示例中学到了很多东西,但是 JPBlac 的答案更符合我的要求。
    • 太棒了!我很高兴你找到了一个好的答案。我会把我的留在这里,以防其他人偶然发现这篇文章并使用我的方法。
    猜你喜欢
    • 2012-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 2013-05-25
    • 1970-01-01
    相关资源
    最近更新 更多