试图覆盖Nokogiri的序列化行为

时间:2011-08-16 19:32:28

标签: html ruby nokogiri

我正在使用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

更新

由于某种原因我无法猜测,当我为我的子类创建一个构造函数时,无论我为子类构造函数需要多少参数,如果我提供任何数字而不是两个(我需要的参数数量),我会收到错误超类)。

>

我没有遇到其他类的问题。我错过了什么吗?

1 个答案:

答案 0 :(得分:1)

最有可能的是,您的代码在某个时间write_toto_html调用serializeserialize调用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