Board logo

标题: [问题求助] PowerShell在xml文件中根据某标签属性值删除其所在的父结点 [打印本页]

作者: 5i365    时间: 2021-12-24 18:41     标题: PowerShell在xml文件中根据某标签属性值删除其所在的父结点

在下面的xml文件中, 我想把rehearsal标签的属性值是ABC所在的父父结点删除, 在论坛和百度没找到确切的答案, 求请高手指点, 非常感谢


如下图所示, 即删除粉红色内的内容



---------------------------------------------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<score-partwise version="3.1">
  <part id="P1">
    <measure number="1" width="118.81">
      <direction placement="above">
        <direction-type>
          <rehearsal default-x="-20.55" relative-y="30.00" font-weight="bold" font-size="14">ABC</rehearsal>
          </direction-type>
        </direction>
      <direction placement="above">
        <direction-type>
          <rehearsal default-x="-40.55" relative-y="60.00" font-weight="bold" font-size="14">CDE</rehearsal>
          </direction-type>
        </direction>
      </measure>
    </part>
  </score-partwise>
作者: idwma    时间: 2021-12-24 19:36

  1. (type a.xml) -join '' -replace '\s+<direction.*>ABC<.*?/direction>','' -replace '>\s',">`r`n"
复制代码

作者: 5i365    时间: 2021-12-24 20:07

回复 2# idwma


    感谢大侠帮忙, 能实现删除效果,
我在代码后面加了下面代码, 生成了新的xml文件,
但有一个问题, 原来的第二行跑到第一行后面了,
具体的实例还没有尝试, 但总感觉用正则删,可能会有些不稳,

| out-file aaa.xml -Encoding default
作者: 5i365    时间: 2021-12-24 20:12

找到一篇文章, 但是水平有限, 看不太懂
https://cloud.tencent.com/developer/ask/138618
作者: idwma    时间: 2021-12-24 20:42

回复 3# 5i365

不稳吗?正则挻通用的万金油
  1. $a=type a.xml
  2. $ofs="`n"
  3. "$a" -replace '\s*<direction[\s\S]*>ABC<[\s\S]*?/direction>',''
复制代码

作者: 5i365    时间: 2021-12-25 08:34

回复 5# idwma


    用notepad2打开输出后的xml, 有下面的提示

作者: 5i365    时间: 2021-12-25 12:34

本帖最后由 5i365 于 2021-12-25 12:50 编辑

回复 5# idwma


按上面链接的代码, 照葫芦画了两个瓢, 但是还是不能删除, 但是$nod可以取到值:
  1. [xml]$xml = Get-Content '.\xml.xml'
  2. $nod = $xml.SelectSingleNode("//rehearsal[contains(text(), 'ABC')]")
  3. $nod
  4. [void]$xml.score-partwise.part.measure.RemoveChild($nod.ParentNode)
  5. $xml.Save('.\ok.xml')
复制代码
------------------------------------------------
  1. $rom, $inp = '.\list.txt', '.\xml.xml'
  2. ($lst, $xml = (Get-Content $rom), [xml](Get-Content $inp))[0].ForEach{
  3. $nod = $xml.SelectSingleNode("//rehearsal[contains(text(), '$_')]")
  4. [void]$xml.score-partwise.part.measure.RemoveChild($nod.ParentNode)
  5. }
  6. $xml.Save('.\ok.xml')
复制代码

作者: xczxczxcz    时间: 2021-12-25 12:48

回复 7# 5i365

不是这样写的。且这样没有通用性,你应该按"ABC"去查找,不宜写死节点名。查找InnerText="ABC"的节点或父节点或爷节点。
作者: 5i365    时间: 2021-12-25 12:51

回复 8# xczxczxcz


    感谢提醒, 但是水平真是有限, 只懂一二, 卡关键点上了
上面链接的例子, 是删父结点, 我的是删爷结点
作者: 5i365    时间: 2021-12-25 15:20

回复 5# idwma


    找到一段代码, 能理解, 但是不会运用
  1. $test = "<task>
  2.    <list>list1</list>
  3.    <list>list2</list>
  4.    <list>list3</list>
  5.    <list>list4</list>
  6.    <list>list5</list>
  7. </task>"
  8. [xml]$myxml = $test
  9. $remove = $myxml.task.SelectSingleNode("//list[.='list2']")
  10. $remove.ParentNode.RemoveChild($remove)
复制代码

作者: 5i365    时间: 2021-12-26 00:36

回复 5# idwma


    试了一个长的xml文件, <text>标签内的中文都成乱码了, 加编码参数也不行
https://wss1.cn/f/77e4p6wnaq6 复制链接到浏览器打开
作者: idwma    时间: 2021-12-26 15:02

本帖最后由 idwma 于 2021-12-26 15:03 编辑

回复 11# 5i365
  1. sc b.xml ([io.file]::ReadAllText("a.xml") -replace '\s*<direction.*>[\r\n\s]*<.*>ABC<[\s\S]*?/direction>','')
复制代码

作者: idwma    时间: 2021-12-26 17:43

本帖最后由 idwma 于 2021-12-26 17:48 编辑

回复 10# 5i365
  1. https://docs.microsoft.com/zh-cn/previous-versions/dotnet/netframework-4.0/ms256086(v=vs.100)
复制代码
  1. [xml]$a=type a.xml
  2. $b=$a.SelectSingleNode("//descendant::direction[direction-type/rehearsal='ABC']")
  3. $b.ParentNode.RemoveChild($b)
  4. $a.save("b.xml")
复制代码

作者: 5i365    时间: 2021-12-26 18:51

回复 13# idwma


    非常感谢!
但是表达式中的限定条件太多了, 而且有时并不知道那些条件, 只知道某个结点的值是ABC, 感觉应该还有更简单的办法
作者: 5i365    时间: 2021-12-26 20:11

回复 13# idwma


刚刚试了上面两楼的代码,
12楼仍然是有乱码
13楼直接报错
作者: idwma    时间: 2021-12-26 20:22

回复 14# 5i365

那这样呢,不需要知道节点的信息了
  1. $a="\s*<.*>[\r\n\s]*<.*>[\r\n\s]*<.*>ABC<.*>[\r\n\s]*<.*>[\r\n\s]*<.*>"
  2. sc b.xml ([io.file]::ReadAllText("a.xml") -replace "$a",'')  -encoding utf8
复制代码

作者: 5i365    时间: 2021-12-26 20:41

回复 12# idwma


    用下面的代码, 可以删除自身所在的结点, 但是没有删除父结点和爷结点
  1. $test = @'
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <score-partwise version="3.1">
  4.   <part id="P1">
  5.     <measure number="1" width="118.81">
  6.       <direction placement="above">
  7.         <direction-type>
  8.           <rehearsal default-x="-20.55" relative-y="30.00" font-weight="bold" font-size="14">ABC</rehearsal>
  9.           </direction-type>
  10.         </direction>
  11.       <direction placement="above">
  12.         <direction-type>
  13.           <rehearsal default-x="-40.55" relative-y="60.00" font-weight="bold" font-size="14">CDE</rehearsal>
  14.           </direction-type>
  15.         </direction>
  16.       </measure>
  17.     </part>
  18.   </score-partwise>
  19. '@
  20. [xml]$a = $test
  21. $b = $a.SelectSingleNode("//rehearsal[contains(text(), 'ABC')]")
  22. $b.ParentNode.RemoveChild($b)
  23. $a.save("b.xml")
复制代码

作者: 5i365    时间: 2021-12-26 20:59

回复 16# idwma


    对正则真是怕了, 如果看不懂, 一点也不会改, 若有特殊字符在多行里面, 真是担心不稳
作者: 5i365    时间: 2021-12-26 21:08

回复 16# idwma


    我的那个网盘的 示例文件 蓝莲花.musicx  后缀虽不同, 但实际是xml文件, 用您的代码[xml]方式加载时, 直接会报错, 好像是提示xml结点有错误, 但是我用前面的代码就不会, 不知道为什么
作者: idwma    时间: 2021-12-26 21:09

回复 17# 5i365

那这样呢
  1. $test = @'
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <score-partwise version="3.1">
  4.   <part id="P1">
  5.     <measure number="1" width="118.81">
  6.       <direction placement="above">
  7.         <direction-type>
  8.           <rehearsal default-x="-20.55" relative-y="30.00" font-weight="bold" font-size="14">ABC</rehearsal>
  9.           </direction-type>
  10.         </direction>
  11.       <direction placement="above">
  12.         <direction-type>
  13.           <rehearsal default-x="-40.55" relative-y="60.00" font-weight="bold" font-size="14">CDE</rehearsal>
  14.           </direction-type>
  15.         </direction>
  16.       </measure>
  17.     </part>
  18.   </score-partwise>
  19. '@
  20. [xml]$a = $test
  21. $b = $a.SelectSingleNode("//*[./.='ABC']")
  22. $b.ParentNode.RemoveChild($b)
  23. $a.save("b.xml")
复制代码

作者: 5i365    时间: 2021-12-26 21:12

本帖最后由 5i365 于 2021-12-26 21:15 编辑

回复 16# idwma


    发现曙光了, 用下面的代码能识别所在标签了, 这样只需要输入 ABC 字符就可以定位了
但是怎样删除爷结点呢?

$b=$a.SelectSingleNode("//*[contains(text(), 'ABC')]")
作者: 5i365    时间: 2021-12-26 21:14

回复 20# idwma


    没有删除爷结点, 只删除了所在的标签, 因为它外面还有两层要删除
作者: 5i365    时间: 2021-12-26 21:16

回复 20# idwma
需要删除的内容如下的红色部分

    <?xml version="1.0" encoding="UTF-8"?>
<score-partwise version="3.1">
  <part id="P1">
    <measure number="1" width="118.81">
      <direction placement="above">
        <direction-type>
          <rehearsal default-x="-20.55" relative-y="30.00" font-weight="bold" font-size="14">ABC</rehearsal>
          </direction-type>
        </direction>

      <direction placement="above">
        <direction-type>
          <rehearsal default-x="-40.55" relative-y="60.00" font-weight="bold" font-size="14">CDE</rehearsal>
          </direction-type>
        </direction>
      </measure>
    </part>
  </score-partwise>
作者: idwma    时间: 2021-12-26 21:19

回复 23# 5i365


    ps 2.0,试了可以删两层
作者: 5i365    时间: 2021-12-26 21:24

回复 24# idwma

我也试了, 下面那个你的是OK的, 上面是我的却不行
$b = $a.SelectSingleNode("//*[contains(text(), 'ABC')]")
$b = $a.SelectSingleNode("//*[./.='ABC']")
作者: 5i365    时间: 2021-12-26 21:28

回复 24# idwma


    但是使用示例文件就不行了, 又是中文部分乱码, 又是直接报错

[xml]$a = type ".\蓝莲花.musicxml"

$b = $a.SelectSingleNode("//*[./.='ABC']")
$b.ParentNode.RemoveChild($b)

$a.save("b.xml")
-------------------------------------------------------
错误信息:
to type "System.Xml.XmlDocument". Error: "The 'text' start tag on line 106 position 12 does not match the end tag of 'lyric'. Line 107, position 13."
作者: 5i365    时间: 2021-12-27 13:35

回复 24# idwma
好像那个限定条件不稳, $text 中的第二行,即红色字部分, 处理后会带上 []

    $test = @'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="3.1">
  <part id="P1">
    <measure number="1" width="118.81">
      <direction placement="above">
        <direction-type>
          <rehearsal default-x="-20.55" relative-y="30.00" font-weight="bold" font-size="14">ABC</rehearsal>
          </direction-type>
        </direction>
      <direction placement="above">
        <direction-type>
          <rehearsal default-x="-40.55" relative-y="60.00" font-weight="bold" font-size="14">CDE</rehearsal>
          </direction-type>
        </direction>
      </measure>
    </part>
  </score-partwise>
'@
[xml]$a = $test

$b = $a.SelectSingleNode("//*[./.='ABC']")
if ($b)
{
        $b.ParentNode.RemoveChild($b)
}
$a.save("ok.xml")
作者: idwma    时间: 2021-12-27 15:11

回复 27# 5i365


    又解锁了新支线任务什么是dtd文件
作者: 5i365    时间: 2021-12-27 17:46

回复 28# idwma


    这行就是 实例文件 蓝莲花.musicxml 中的第二行

这是一个我做的音乐谱子文件, 里面有特定的标签 ABC 都做错了,

我可以打开谱子软件去手动删除它, 但是我还要去找, 所以就右键用记事本打开看了一下, 发现是xml文件, 然后印象中powershell可以处理xml文件, 就找了一些贴子和资料,想试试, 能不能用powershell删除那个标签

要处理的谱子文件不是很多, 手动删除半个小时也能做完, 但对powershell感点兴趣, 也顺便看看能不能让脚本能力得到提升
作者: idwma    时间: 2021-12-27 18:38

回复 29# 5i365


   技穷了,这个乐谱文件解析时还出错
作者: 5i365    时间: 2021-12-27 19:19

回复 30# idwma
这样解析时不报错了

    [xml]$a = Get-Content ".\蓝莲花.musicxml" -Encoding UTF8
作者: 5i365    时间: 2021-12-27 19:22

回复 30# idwma

试了一下, 这样可以找到爷结点, 但是还是删除不干净, 还是留下了标签

$b = $a.SelectSingleNode("//rehearsal[text()='ABC']")

$b.ParentNode.ParentNode.RemoveAll()
作者: 5i365    时间: 2021-12-27 19:24

回复  5i365

那这样呢,不需要知道节点的信息了
idwma 发表于 2021-12-26 20:22



    这种不稳, 实例中, 把好多地方都删除了
作者: 5i365    时间: 2021-12-27 19:27

回复 30# idwma


    我感觉下面这种可能也不稳, 所以想找上面那种结点的方法来删除:
$b = $a.SelectSingleNode("//*[./.='ABC']")
作者: 5i365    时间: 2021-12-27 19:47

本帖最后由 5i365 于 2021-12-27 19:49 编辑

回复 30# idwma


实例中下面这种匹配真出错了 , 我不再找文本是 ABC 的,换成了找是 A 的, 结果把别的地方的A也删除了,

$b = $a.SelectSingleNode("//*[./.='A']"

看来至少要限定A所在的标签, 下面这样可以定位所在标签了, 但没有办法删除爷标签 :

$b = $a.SelectSingleNode("//rehearsal[./.='A']"
作者: idwma    时间: 2021-12-27 22:53

回复 35# 5i365
  1. $a.SelectSingleNode("//*[*/rehearsal='A']")
复制代码

作者: 5i365    时间: 2021-12-28 08:15

回复 36# idwma


    如何让a这个文本不区分大小写? 这个只能找小a
$a.SelectSingleNode("//*[*/rehearsal='a']")
作者: idwma    时间: 2021-12-28 15:18

回复 37# 5i365

搜一下就有了照抄了
     https://developer.mozilla.org/en ... Functions/translate
  1. $a.SelectSingleNode("//*[*/rehearsal=translate('a', 'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')]")
复制代码

作者: 5i365    时间: 2021-12-28 18:18

回复 38# idwma


    多谢




欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2