Class: Nokogiri::XSLT::Stylesheet

Inherits:
Object
  • Object
show all
Defined in:
lib/nokogiri/xslt/stylesheet.rb,
ext/nokogiri/xslt_stylesheet.c

Overview

A Stylesheet represents an XSLT Stylesheet object. Stylesheet creation is done through Nokogiri::XSLT.parse (or the convenience method 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

🛡 This class does not support execution of untrusted stylesheets. An untrusted stylesheet may consume a large amount of CPU, memory, or other system resources during transformation, and IO and file access are not restricted. See Nokogiri::XSLT for more information about the security implications of untrusted stylesheets.

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.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.parse_stylesheet_doc(document) ⇒ Object

Parse an XSLT::Stylesheet from document.

[Parameters]

  • document (Nokogiri::XML::Document) the document to be parsed.

[Returns] Nokogiri::XSLT::Stylesheet



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'ext/nokogiri/xslt_stylesheet.c', line 81

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);
}

Instance Method Details

#apply_to(document, params = []) ⇒ Object

:call-seq:

apply_to(document, params = []) -> String

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]

  • document is an instance of XML::Document to transform
  • params is an array of strings used as XSLT parameters, passed into #transform

[Returns] A string containing the serialized result of the transformation.

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



49
50
51
# File 'lib/nokogiri/xslt/stylesheet.rb', line 49

def apply_to(document, params = [])
  serialize(transform(document, params))
end

#serialize(document) ⇒ Object

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



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'ext/nokogiri/xslt_stylesheet.c', line 114

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,
    &nokogiri_xslt_stylesheet_tuple_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) ⇒ Object #transform(document, params = {}) ⇒ Object

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

[Parameters]

  • document (Nokogiri::XML::Document) the document to be transformed.
  • params (Hash, Array) strings used as XSLT 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



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'ext/nokogiri/xslt_stylesheet.c', line 278

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 ;
  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, &nokogiri_xslt_stylesheet_tuple_type, wrapper);

  param_len = RARRAY_LEN(rb_param);
  params = ruby_xcalloc((size_t)param_len + 1, sizeof(char *));
  {
    // populate params under rb_protect so that a raise from StringValueCStr
    // (e.g. on a null byte) does not leak the params allocation.
    build_xslt_params_args_t args = { rb_param, param_len, params };
    int state = 0;

    rb_protect(build_xslt_params, (VALUE)&args, &state);
    if (state) {
      ruby_xfree(params);
      rb_jump_tag(state);
    }
  }
  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) ;
}