使用 Ruby & Nokogiri 向现有 XML 文件添加新的子节点



我用Ruby编写了这个服务器项目,我想在XML文件中跟踪事件和用户会话。我对这个完全陌生,经过几天的研究,我碰壁了。

下面是我当前的示例代码,假设已经有一个名为"test.xml"的文件,其中包含一个名为 的根节点
$ cat test.xml
<server></server>

和代码:

require 'nokogiri'
require 'securerandom'
logintime = Time.now
sessionid = SecureRandom.hex(10)
file = File.open("test.xml",'a+')
doc = Nokogiri::XML.parse file
session_node = Nokogiri::XML::Node.new("session",doc)
session_node['id'] = sessionid
logintime_node = Nokogiri::XML::Node.new("logintime",doc)
logintime_node.content = logintime
session_node << logintime_node
doc.root << session_node
file.print doc.to_xml
file.close
下面是运行4次后的test.xml文件
<server></server>
<?xml version="1.0"?>
<server>
  <session id="5ef27ade2afaf5c2162f">
    <logintime>2015-07-07 17:27:20 +0200</logintime>
  </session>
</server>
<?xml version="1.0"?>
<server>
  <session id="637595bd0857c8af1cc0">
    <logintime>2015-07-07 17:27:36 +0200</logintime>
  </session>
</server>
<?xml version="1.0"?>
<?xml version="1.0"?>
<server>
  <session id="41e6082c4db7d1dc8692">
    <logintime>2015-07-07 17:27:37 +0200</logintime>
  </session>
</server>
<?xml version="1.0"?>
<?xml version="1.0"?>
<server>
  <session id="1cad6c3d38d4fb96632b">
    <logintime>2015-07-07 17:27:38 +0200</logintime>
  </session>
</server>
<?xml version="1.0"?>

期望的输出应该是这样的:

<?xml version="1.0"?>
<server>
  <session id="5ef27ade2afaf5c2162f">
    <logintime>2015-07-07 17:27:20 +0200</logintime>
  </session>
  <session id="637595bd0857c8af1cc0">
    <logintime>2015-07-07 17:27:36 +0200</logintime>
  </session>
  <session id="41e6082c4db7d1dc8692">
    <logintime>2015-07-07 17:27:37 +0200</logintime>
  </session>
  <session id="1cad6c3d38d4fb96632b">
    <logintime>2015-07-07 17:27:38 +0200</logintime>
  </session>
</server>

我真的不知道我为什么要得到那个结果。

首先,如果不存在包含根节点的现有文件,则脚本只运行一次,然后在我第二次尝试运行它时抱怨已经存在根节点:

/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/gems/2.0.0/gems/nokogiri-1.5.6/lib/nokogiri/xml/document.rb:232:in `add_child': Document already has a root node (RuntimeError)
    from /Users/xxx/nokogiri.rb:13:in `<top (required)>'
    from -e:1:in `load'
    from -e:1:in `<main>'

所以…我有点迷路了。有什么想法吗?

问题是您使用File.open('test.xml', 'a+')以追加模式打开文件,然后使用file.print doc.to_xml将整个XML文档写入其中。这就是为什么整个文档会被多次写入文件的原因。

如果独立读写文件,XML文档将以您想要的方式替换文件。如果你需要处理的文件还不存在,你也可以检查它,并初始化数据与您的<server>根标签。

require 'nokogiri'
require 'securerandom'
logintime = Time.now
sessionid = SecureRandom.hex(10)
# Read or initialize the data
if File.exist?('test.xml')
  data = File.read("test.xml")
else
  data = '<server></server>'
end
doc = Nokogiri::XML.parse data
session_node = Nokogiri::XML::Node.new("session",doc)
session_node['id'] = sessionid
logintime_node = Nokogiri::XML::Node.new("logintime",doc)
logintime_node.content = logintime
session_node << logintime_node
doc.root << session_node
# Write the document to disk
File.open('test.xml', 'w') do |file|
  file.print doc.to_xml
end

我不建议长时间以这种方式记录会话。在任何重要的用户负载下,写入文件都将变得非常昂贵。此外,如果您有多个服务器在运行,那么它们都将从另一个服务器下输出文件。当您达到这一点时,至少应该将存储转换为数据库,或者更好地使用为此构建的ELK Stack。

相关内容

  • 没有找到相关文章

最新更新