使用REXML保留DOCTYPE声明

时间:2011-11-02 15:09:19

标签: ruby rexml

我正在尝试解析log4j.xml文件,编辑一些属性并将其写回。

log4j.xml具有<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">声明,但当我将其写回时,声明更改为<!DOCTYPE log4j>

我已打开文件以便使用xmlDoc = Document.new(File.new(file, 'r'))进行解析,并使用xmlDoc.write(File.new(file, 'w'), 0)进行了编写。

我也试过用xmlDoc = Document.new(File.new(file, 'r'), { :raw => :all })打开。

有没有办法保留原始DOCTYPE声明?

非常感谢!

1 个答案:

答案 0 :(得分:0)

我担心使用rexml是不可能的。看一下这个简短的摘要 - 这是rexml库中发生的过程的“轻量级版本”

require 'rexml/source'

LETTER = '[:alpha:]'
COMBININGCHAR = ''
EXTENDER = ''
NCNAME_STR= "[#{LETTER}_:][-[:alnum:]._:#{COMBININGCHAR}#{EXTENDER}]*"

IDENTITY = /^([!\*\w\-]+)(\s+#{NCNAME_STR})?(\s+["'](.*?)['"])?(\s+['"](.*?)["'])?/u
DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um

string = <<HERE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>
</log4j:configuration>
HERE
source = REXML::SourceFactory.create_from(string)
md = source.match( DOCTYPE_PATTERN, true )
identity = md[1]
close = md[2]
identity =~ IDENTITY
name = $1
pub_sys = $2.nil? ? nil : $2.strip
long_name = $4.nil? ? nil : $4.strip
uri = $6.nil? ? nil : $6.strip
args = [ :start_doctype, name, pub_sys, long_name, uri ]
p args  # => [:start_doctype, "log4j", nil, nil, nil]

正如您所看到的,此代码段会返回与问题中的代码相同的结果。除此之外,您还会看到代码段中没有可以更改此行为的参数。

作为一种解决方法,我建议您使用Nokogiri库。快速查看它可以正确解析这样的文档类型:

require 'nokogiri'

string = <<HERE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>
</log4j:configuration>
HERE

doc = Nokogiri::XML(string)
puts doc.internal_subset.to_s
# => <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">