在Ruby中解析和处理XML

2 投票
2 回答
970 浏览
提问于 2025-04-16 08:15

我有一个小的XML文档,叫做foobar.xml。

<?xml version="1.0" standalone="no"?>
<!DOCTYPE foo SYSTEM "bar.dtd">
<title _FORMAT="XXX.XXX" _QUANTITY="1" _DEVICENAME="XXX" _JOBNAME="FOOBAR">
    <subtitle>
    <variable name="x">A-1234567</variable>
    </subtitle>
</title>

还有一点Python代码,

    with open('foobar.xml', 'rt') as f:
        tree = ElementTree.parse(f)
    # Loop over all elements in 'tree' in section order
    for node in tree.getiterator():
        if node.tag == "variable":
            for z in range(number):
                if len(str(start)) == 7:
                    accession = "A-" + str(start)
                # This simply adds leading zeros if there are < 7 digits
                elif len(str(start)) < 7:
                    accession = "A-" + ("0" * (7 - len(str(start))) + str(start))
                start += 1
                # Assign 'accession' to the node text
                node.text = accession
                tree.write("foobar.xml")
        else:
            continue

这段代码可以很漂亮地找到我感兴趣的节点标签,并通过循环来处理它,每次都替换节点的文本,然后把XML写入一个文件。不过,有一个问题:我需要用Ruby来实现这个功能。

到目前为止,我有

doc = Document.new(File.new("foobar.xml"))
doc.elements.each() do |element|
  element.elements.each() do |child|
    child.elements.each() do |sub| # probably wrong
      for z in 0...$number
        if $start.to_s.length == 7 
          accession = "A-" + $start.to_s
        else
          accession = "A-" + ("0" * (7 - $start.to_s.length)) + $start.to_s
        end
      $start += 1
      # need to assign here and write to file or assign to variable
      end
    end
  end
end

这是我第一次在Ruby中处理XML,我对语法真的不太理解。我的目标基本上是复制Python的做法,通过循环每次更改节点文本,然后把结果写入一个XML文件。任何建议都非常感谢。

2 个回答

2

我更喜欢用Nokogiri来解析Ruby中的XML;它使用起来又快又高效(而且如果你愿意的话,可以用类似CSS的选择器,而不是XPath):

require 'nokogiri'
$number = 3
$start  = 134341

my_xml = IO.read('foobar.xml')
doc = Nokogiri::XML(my_xml)
doc.css('variable').each do |el|
  $number.times do
    # Pads to a 7-digit number: see `ri Kernel#sprintf`
    el.content = "A-%07d" % $start
    File.open( "foobar-#{$start}.xml", 'w' ) do |f|
      f << doc
    end
    $start += 1
  end
end

我对上面的代码做了一些修改,让它可以输出一个独特的文件;你肯定不会一直写同一个文件,对吧?

1

为了完整性,这里有一个基于REXML的解决方案:

require 'rexml/document'
$number = 3
$start  = 1312

doc = REXML::Document.new(my_xml)
REXML::XPath.each(doc,'//variable') do |el|
  $number.times do
    el.text = "A-%07d" % $start
    File.open( "f-#{$start}.xml", 'w' ){ |f| f << doc }
    $start += 1
  end
end

撰写回答