我使用Nokogiri修改HTML树并输出代码。我需要改变一个特定节点输出到html的方式(详情如下),所以我已经子类化了Nokogiri::XML::Node
。
我如何重写子类的输出行为?
现在,如果我覆盖to_html()
,那么我在为Nokogiri::HTML::DocumentFragment
实例调用to_html()
时得到我想要的显示,但是当我在Nokogiri::HTML::Document
实例上调用它时,正常的输出行为接管。这样做是不行的,因为我实际上需要更改文档头部(它被排除在DocumentFragment实例之外)。
为什么我需要改变HTML输出:
我需要能够包括一个非合作的</noscript>
标签,为了使用GWO与我的代码。但是,我不能在HTML树中添加非合作结束标记。
使用Nokogiri,我也不能将其添加为文本,因为<
和>
被转义为html字符代码。
我不能在这个项目中使用Hpricot,因为我在一些糟糕的代码(由其他人在工作中编写)上运行它,并且Hpricot不会保留有问题的错误(例如将块元素放在<a>
元素中)。(不,我不打算追踪所有糟糕的HTML并修复它。)
规格: WinXP, Ruby 1.8.6, Nokogiri 1.4.4
更新:
由于我无法猜测的原因,当我为子类创建构造函数时,无论我为子类构造函数需要多少参数,如果我提供了两个以外的任何数字(超类所需的参数数量),我都会得到错误。
class NoScript < Nokogiri::XML::Node
def initialize(doc)
super("string", doc)
end
end
我在其他课程中没有遇到过这个问题。我错过什么了吗?
最有可能的是,您的代码在某个点调用write_to
(to_html
调用serialize
, serialize
调用write_to
)。然后在当前节点上调用native_write_to
。让我们来看看。
static VALUE native_write_to(
VALUE self,
VALUE io,
VALUE encoding,
VALUE indent_string,
VALUE options
) {
xmlNodePtr node;
const char * before_indent;
xmlSaveCtxtPtr savectx;
Data_Get_Struct(self, xmlNode, node);
xmlIndentTreeOutput = 1;
before_indent = xmlTreeIndentString;
xmlTreeIndentString = StringValuePtr(indent_string);
savectx = xmlSaveToIO(
(xmlOutputWriteCallback)io_write_callback,
(xmlOutputCloseCallback)io_close_callback,
(void *)io,
RTEST(encoding) ? StringValuePtr(encoding) : NULL,
(int)NUM2INT(options)
);
xmlSaveTree(savectx, node);
xmlSaveClose(savectx);
xmlTreeIndentString = before_indent;
return io;
}
代码在github。如果您阅读它,您将看到它没有在任何地方调用to_html,因此您的自定义方法永远不会运行。如果你使用Nokogiri::HTML::DocumentFragment
,它会被调用,因为DocumentFragment#to_html
依赖于Nokogiri::XML::NodeSet#to_html
,它是一个普通的映射:
def to_html *args
if Nokogiri.jruby?
options = args.first.is_a?(Hash) ? args.shift : {}
if !options[:save_with]
options[:save_with] = Node::SaveOptions::NO_DECLARATION | Node::SaveOptions::NO_EMPTY_TAGS | Node::SaveOptions::AS_HTML
end
args.insert(0, options)
end
map { |x| x.to_html(*args) }.join
end