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

Adding new child nodes to an existing XML file with Ruby & Nokogiri

我在 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 运行s

后的 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 之类的东西。