Попытка переопределить сериализационное поведение Нокогири - PullRequest
1 голос
/ 16 августа 2011

Я использую Nokogiri для изменения дерева HTML и вывода кода.Мне нужно изменить способ вывода определенного узла на html (подробности ниже), поэтому я выделил подклассы Nokogiri::XML::Node.

Как переопределить поведение вывода этого подкласса?

Прямо сейчас, если я переопределяю to_html(), то получаю нужное отображение при вызове to_html() для экземпляров Nokogiri::HTML::DocumentFragment, но когда я вызываю его в случаях 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

У меня не было этой проблемы с другими классами.Я что-то упустил?

1 Ответ

1 голос
/ 17 августа 2011

Скорее всего, ваш код звонит в какой-то момент 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
...