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'))

puts xslt.transform(doc)

See Nokogiri::XSLT::Stylesheet#transform for more transformation information.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.parse_stylesheet_doc(document) ⇒ Object

Parse a stylesheet from document.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'ext/nokogiri/xslt_stylesheet.c', line 76

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

Apply an XSLT stylesheet to an XML::Document. params is an array of strings used as XSLT parameters. returns serialized document



22
23
24
# File 'lib/nokogiri/xslt/stylesheet.rb', line 22

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

#serialize(document) ⇒ Object

Serialize document to an xml string.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'ext/nokogiri/xslt_stylesheet.c', line 109

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

Apply an XSLT stylesheet to an XML::Document.

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



244
245
246
247
248
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
# File 'ext/nokogiri/xslt_stylesheet.c', line 244

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