我正在使用Nokogiri来修改HTML树并输出代码。我需要改变特定节点输出到html的方式(详情如下),所以我已经将Nokogiri::XML::Node
子类化了。
如何覆盖该子类的输出行为?
现在,如果我覆盖to_html()
,那么在为to_html()
的实例调用Nokogiri::HTML::DocumentFragment
时,我会得到我想要的显示,但是当我在{{1}的实例上调用它时,正常的输出行为接管。这是行不通的,因为我实际上需要对文档头进行更改(从DocumentFragment实例中排除)。
为什么我需要更改HTML输出:
为了在我的代码中使用GWO,我需要能够包含一个非合作的Nokogiri::HTML::Document
标记。但是,我无法在HTML树中添加未结合的结束标记。
使用Nokogiri,我无法将其添加为文本,因为</noscript>
和<
会被转义为html字符代码。
我不能在这个项目中使用Hpricot,因为我在一些不好的代码上运行它(在工作中由其他人编写),并且Hpricot不会保留有问题的错误(比如将一个块元素放在一个{ {1}}元素)。 (不,我不打算追查所有糟糕的HTML并修复它。)
规格: WinXP,Ruby 1.8.6,Nokogiri 1.4.4
更新
由于某种原因我无法猜测,当我为我的子类创建一个构造函数时,无论我为子类构造函数需要多少参数,如果我提供任何数字而不是两个(我需要的参数数量),我会收到错误超类)。
>
我没有遇到其他类的问题。我错过了什么吗?
答案 0 :(得分:1)
最有可能的是,您的代码在某个时间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,因此您的自定义方法永远不会运行。 OTOH,如果您使用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