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. 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.

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.



44
45
46
# File 'lib/nokogiri/xslt/stylesheet.rb', line 44

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



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
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
# File 'ext/nokogiri/xslt_stylesheet.c', line 249

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, &nokogiri_xslt_stylesheet_tuple_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) ;
}