class Nokogiri::XSLT::Stylesheet

A Stylesheet represents an XSLT Stylesheet object. Stylesheet creation is done through Nokogiri.XSLT. Here is an example of transforming an XML::Document with a Stylesheet:

doc   = Nokogiri::XML(File.read('some_file.xml'))
xslt  = Nokogiri::XSLT(File.read('some_transformer.xslt'))

xslt.transform(doc) # => Nokogiri::XML::Document

Many XSLT transformations include serialization behavior to emit a non-XML document. For these cases, please take care to invoke the #serialize method on the result of the transformation:

doc   = Nokogiri::XML(File.read('some_file.xml'))
xslt  = Nokogiri::XSLT(File.read('some_transformer.xslt'))
xslt.serialize(xslt.transform(doc)) # => String

or use the #apply_to method, which is a shortcut for ‘serialize(transform(document))`:

doc   = Nokogiri::XML(File.read('some_file.xml'))
xslt  = Nokogiri::XSLT(File.read('some_transformer.xslt'))
xslt.apply_to(doc) # => String

See Nokogiri::XSLT::Stylesheet#transform for more information and examples.

Public Class Methods

parse_stylesheet_doc(document) click to toggle source

Parse an XSLT::Stylesheet from document.

Parameters
Returns

Nokogiri::XSLT::Stylesheet

static VALUE
parse_stylesheet_doc(VALUE klass, VALUE xmldocobj)
{
  xmlDocPtr xml, xml_cpy;
  VALUE errstr, exception;
  xsltStylesheetPtr ss ;

  xml = noko_xml_document_unwrap(xmldocobj);

  errstr = rb_str_new(0, 0);
  xsltSetGenericErrorFunc((void *)errstr, xslt_generic_error_handler);

  xml_cpy = xmlCopyDoc(xml, 1); /* 1 => recursive */
  ss = xsltParseStylesheetDoc(xml_cpy);

  xsltSetGenericErrorFunc(NULL, NULL);

  if (!ss) {
    xmlFreeDoc(xml_cpy);
    exception = rb_exc_new3(rb_eRuntimeError, errstr);
    rb_exc_raise(exception);
  }

  return Nokogiri_wrap_xslt_stylesheet(ss);
}

Public Instance Methods

apply_to(document, params = []) → String click to toggle source

Apply an XSLT stylesheet to an XML::Document and serialize it properly. This method is equivalent to calling #serialize on the result of #transform.

Parameters
Returns

A string containing the serialized result of the transformation.

See Nokogiri::XSLT::Stylesheet#transform for more information and examples.

# File lib/nokogiri/xslt/stylesheet.rb, line 44
def apply_to(document, params = [])
  serialize(transform(document, params))
end
serialize(document) click to toggle source

Serialize document to an xml string, as specified by the method parameter in the Stylesheet.

static VALUE
rb_xslt_stylesheet_serialize(VALUE self, VALUE xmlobj)
{
  xmlDocPtr xml ;
  nokogiriXsltStylesheetTuple *wrapper;
  xmlChar *doc_ptr ;
  int doc_len ;
  VALUE rval ;

  xml = noko_xml_document_unwrap(xmlobj);
  TypedData_Get_Struct(
    self,
    nokogiriXsltStylesheetTuple,
    &xslt_stylesheet_type,
    wrapper
  );
  xsltSaveResultToString(&doc_ptr, &doc_len, xml, wrapper->ss);
  rval = NOKOGIRI_STR_NEW(doc_ptr, doc_len);
  xmlFree(doc_ptr);
  return rval ;
}
transform(document) click to toggle source
transform(document, params = {})

Transform an XML::Document as defined by an XSLT::Stylesheet.

Parameters
Returns

Nokogiri::XML::Document

Example of basic transformation:

xslt = <<~XSLT
  <xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:param name="title"/>

  <xsl:template match="/">
    <html>
      <body>
        <h1><xsl:value-of select="$title"/></h1>
        <ol>
          <xsl:for-each select="staff/employee">
            <li><xsl:value-of select="employeeId"></li>
          </xsl:for-each>
        </ol>
      </body>
    </html>
  </xsl:stylesheet>
XSLT

xml = <<~XML
  <?xml version="1.0"?>
  <staff>
    <employee>
      <employeeId>EMP0001</employeeId>
      <position>Accountant</position>
    </employee>
    <employee>
      <employeeId>EMP0002</employeeId>
      <position>Developer</position>
    </employee>
  </staff>
XML

doc = Nokogiri::XML::Document.parse(xml)
stylesheet = Nokogiri::XSLT.parse(xslt)

⚠ Note that the h1 element is empty because no param has been provided!

stylesheet.transform(doc).to_xml
# => "<html><body>\n" +
#    "<h1></h1>\n" +
#    "<ol>\n" +
#    "<li>EMP0001</li>\n" +
#    "<li>EMP0002</li>\n" +
#    "</ol>\n" +
#    "</body></html>\n"

Example of using an input parameter hash:

⚠ The title is populated, but note how we need to quote-escape the value.

stylesheet.transform(doc, { "title" => "'Employee List'" }).to_xml
# => "<html><body>\n" +
#    "<h1>Employee List</h1>\n" +
#    "<ol>\n" +
#    "<li>EMP0001</li>\n" +
#    "<li>EMP0002</li>\n" +
#    "</ol>\n" +
#    "</body></html>\n"

Example using the XSLT.quote_params helper method to safely quote-escape strings:

stylesheet.transform(doc, Nokogiri::XSLT.quote_params({ "title" => "Aaron's List" })).to_xml
# => "<html><body>\n" +
#    "<h1>Aaron's List</h1>\n" +
#    "<ol>\n" +
#    "<li>EMP0001</li>\n" +
#    "<li>EMP0002</li>\n" +
#    "</ol>\n" +
#    "</body></html>\n"

Example using an array of XSLT parameters

You can also use an array if you want to.

stylesheet.transform(doc, ["title", "'Employee List'"]).to_xml
# => "<html><body>\n" +
#    "<h1>Employee List</h1>\n" +
#    "<ol>\n" +
#    "<li>EMP0001</li>\n" +
#    "<li>EMP0002</li>\n" +
#    "</ol>\n" +
#    "</body></html>\n"

Or pass an array to XSLT.quote_params:

stylesheet.transform(doc, Nokogiri::XSLT.quote_params(["title", "Aaron's List"])).to_xml
# => "<html><body>\n" +
#    "<h1>Aaron's List</h1>\n" +
#    "<ol>\n" +
#    "<li>EMP0001</li>\n" +
#    "<li>EMP0002</li>\n" +
#    "</ol>\n" +
#    "</body></html>\n"

See: Nokogiri::XSLT.quote_params

static VALUE
rb_xslt_stylesheet_transform(int argc, VALUE *argv, VALUE self)
{
  VALUE rb_document, rb_param, rb_error_str;
  xmlDocPtr c_document ;
  xmlDocPtr c_result_document ;
  nokogiriXsltStylesheetTuple *wrapper;
  const char **params ;
  long param_len, j ;
  int parse_error_occurred ;
  int defensive_copy_p = 0;

  rb_scan_args(argc, argv, "11", &rb_document, &rb_param);
  if (NIL_P(rb_param)) { rb_param = rb_ary_new2(0L) ; }
  if (!rb_obj_is_kind_of(rb_document, cNokogiriXmlDocument)) {
    rb_raise(rb_eArgError, "argument must be a Nokogiri::XML::Document");
  }

  /* handle hashes as arguments. */
  if (T_HASH == TYPE(rb_param)) {
    rb_param = rb_funcall(rb_param, rb_intern("to_a"), 0);
    rb_param = rb_funcall(rb_param, rb_intern("flatten"), 0);
  }

  Check_Type(rb_param, T_ARRAY);

  c_document = noko_xml_document_unwrap(rb_document);
  TypedData_Get_Struct(self, nokogiriXsltStylesheetTuple, &xslt_stylesheet_type, wrapper);

  param_len = RARRAY_LEN(rb_param);
  params = ruby_xcalloc((size_t)param_len + 1, sizeof(char *));
  for (j = 0 ; j < param_len ; j++) {
    VALUE entry = rb_ary_entry(rb_param, j);
    const char *ptr = StringValueCStr(entry);
    params[j] = ptr;
  }
  params[param_len] = 0 ;

  xsltTransformContextPtr c_transform_context = xsltNewTransformContext(wrapper->ss, c_document);
  if (xsltNeedElemSpaceHandling(c_transform_context) &&
      noko_xml_document_has_wrapped_blank_nodes_p(c_document)) {
    // see https://github.com/sparklemotion/nokogiri/issues/2800
    c_document = xmlCopyDoc(c_document, 1);
    defensive_copy_p = 1;
  }
  xsltFreeTransformContext(c_transform_context);

  rb_error_str = rb_str_new(0, 0);
  xsltSetGenericErrorFunc((void *)rb_error_str, xslt_generic_error_handler);
  xmlSetGenericErrorFunc((void *)rb_error_str, xslt_generic_error_handler);

  c_result_document = xsltApplyStylesheet(wrapper->ss, c_document, params);

  ruby_xfree(params);
  if (defensive_copy_p) {
    xmlFreeDoc(c_document);
    c_document = NULL;
  }

  xsltSetGenericErrorFunc(NULL, NULL);
  xmlSetGenericErrorFunc(NULL, NULL);

  parse_error_occurred = (Qfalse == rb_funcall(rb_error_str, rb_intern("empty?"), 0));

  if (parse_error_occurred) {
    rb_exc_raise(rb_exc_new3(rb_eRuntimeError, rb_error_str));
  }

  return noko_xml_document_wrap((VALUE)0, c_result_document) ;
}