如果XML对象包含特定值,请将其删除

时间:2013-10-09 00:33:38

标签: xml vim csv xml-parsing

我有一个包含对象的大型XML文档(13MB)(包含产品SKU,名称等的产品)。

对于每个应从我的XML文档中删除的产品,我还有一个产品SKU的大型列表(CSV,700项)。

如果XML文档中的产品包含我的列表中的SKU,我想从XML文档中删除整个产品/对象。

以下是XML结构的示例:

<product>
  <Product_ID><![CDATA[1]]></Product_ID>
  <Product_Name><![CDATA[First product]]></Product_Name>
  <Product_CodeSKU><![CDATA[0000001]]></Product_CodeSKU>
</product>
<product>
  <Product_ID><![CDATA[2]]></Product_ID>
  <Product_Name><![CDATA[Second product]]></Product_Name>
  <Product_CodeSKU><![CDATA[0000002]]></Product_CodeSKU>
</product>
<product>
  <Product_ID><![CDATA[3]]></Product_ID>
  <Product_Name><![CDATA[Third product]]></Product_Name>
  <Product_CodeSKU><![CDATA[0000003]]></Product_CodeSKU>
</product>   

我的列表(CSV)包含“0000001”之类的值。我想找到任何包含该值的产品,并将其删除 - 同时保持其他产品不受影响。

3 个答案:

答案 0 :(得分:2)

好的,首先:如果我不止一次这样做,那么我会以一种完全自动化的方式为我做一个漂亮的Perl脚本。然后其他人也可以使用它,而不仅仅是我们的Vim书呆子。我说Perl,因为这就是我所知道的;其他人会说python或Ruby或者他们所选择的语言,但是无论如何:对于重复使用,Vim是错误的工具。

然而,有时你只是想快速完成某件事,这可能不会是一项非常普遍的任务。

要在Vim中解决这个问题,我会在XML文件旁边的分割缓冲区中打开CSV。

  1. 将光标放在CSV文件的第一行。
  2. 开始将宏录制到您选择的寄存器中。例如,qa
  3. 执行一个通用命令,无论您在哪条线上,都始终将光标定位在SKU上以便移除。
  4. yiw(或其他一些命令来拉动整个SKU)。
  5. <C-W>w将窗口切换为XML文件。
  6. :g#<C-R>0启动a:g命令并插入SKU作为模式。如果存在误报,您可能需要调整此选项以仅匹配真实的SKU行。
  7. 使用常规命令完成:g命令以删除整个标记,例如:g#0000001#norm! vatatVd
  8. 使用<C-w>p
  9. 返回CSV文件
  10. 使用j
  11. 移至CSV文件中的下一行
  12. 使用q停止录制宏。
  13. 测试重复宏一次(例如,@a)。
  14. 如果测试失败,请重复步骤1-10,直到获得可重复的宏。
  15. 如果(10)按预期工作,则根据需要重复多次,例如9999999@a

答案 1 :(得分:2)

在Vimscript中,假设一个基于UNIX的系统:

fun! ClearSKUs()
  let command = "cat " . input("Enter path to CSV file: ")
  let data = system(command) | redraw!
  let values = split(substitute(data, "\n", "", ""), ",")
  for value in values
    if search(value) > 0
      silent ?<product?,/<\/product/d
    endif
  endfor
endfun

command! ClearSKUs call ClearSKUs()

要运行,在编辑XML文档时,请使用:

:ClearSKUs

编辑已更新,将match()替换为search()作为@Ben提及,并使用单个普通命令。

编辑2:已更新,将正常命令替换为d的范围(谢谢,@ Ben!)并在收到输入后清除提示。

答案 2 :(得分:2)

嗯,这是一个XSLT 2.0解决方案:

<xsl:stylesheet...>

<xsl:variable name="removals" select="tokenize(unparsed-text('skus.csv'), '\n')"/> 

<xsl:template match="*">
  <xsl:copy><xsl:apply-templates/></xsl:copy>
</xsl:template>

<xsl:template match="product[Product_CodeSKU = $removals]"/>

</xsl:stylesheet>

似乎比@ Ben的解决方案简单得多,而且速度要快得多。

可能需要进行一些调整,因为我不清楚CSV文件的格式。