class Nokogiri::XML::NodeSet
A NodeSet
contains a list of Nokogiri::XML::Node
objects. Typically a NodeSet
is return as a result of searching a Document
via Nokogiri::XML::Searchable#css
or Nokogiri::XML::Searchable#xpath
Constants
- LOOKS_LIKE_XPATH
-
Included from Nokogiri::XML::Searchable
Regular expression used by
Searchable#search
to determine if a query string isCSS
orXPath
Attributes
Public Class Methods
Create a NodeSet
with document
defaulting to list
# File lib/nokogiri/xml/node_set.rb, line 20 def initialize(document, list = []) @document = document document.decorate(self) list.each { |x| self << x } yield self if block_given? end
Public Instance Methods
Set Intersection — Returns a new NodeSet
containing nodes common to the two NodeSets.
static VALUE intersection(VALUE rb_self, VALUE rb_other) { xmlNodeSetPtr c_self, c_other ; xmlNodeSetPtr intersection; if (!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet)) { rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet"); } TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); TypedData_Get_Struct(rb_other, xmlNodeSet, &xml_node_set_type, c_other); intersection = xmlXPathIntersection(c_self, c_other); return noko_xml_node_set_wrap(intersection, rb_iv_get(rb_self, "@document")); }
Difference - returns a new NodeSet
that is a copy of this NodeSet
, removing each item that also appears in node_set
static VALUE minus(VALUE rb_self, VALUE rb_other) { xmlNodeSetPtr c_self, c_other; xmlNodeSetPtr new; int j ; if (!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet)) { rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet"); } TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); TypedData_Get_Struct(rb_other, xmlNodeSet, &xml_node_set_type, c_other); new = xmlXPathNodeSetMerge(NULL, c_self); for (j = 0 ; j < c_other->nodeNr ; ++j) { xpath_node_set_del(new, c_other->nodeTab[j]); } return noko_xml_node_set_wrap(new, rb_iv_get(rb_self, "@document")); }
Equality – Two NodeSets are equal if the contain the same number of elements and if each element is equal to the corresponding element in the other NodeSet
# File lib/nokogiri/xml/node_set.rb, line 393 def ==(other) return false unless other.is_a?(Nokogiri::XML::NodeSet) return false unless length == other.length each_with_index do |node, i| return false unless node == other[i] end true end
Element
reference - returns the node at index
, or returns a NodeSet
containing nodes starting at start
and continuing for length
elements, or returns a NodeSet
containing nodes specified by range
. Negative indices
count backward from the end of the node_set
(-1 is the last node). Returns nil if the index
(or start
) are out of range.
static VALUE slice(int argc, VALUE *argv, VALUE rb_self) { VALUE arg ; long beg, len ; xmlNodeSetPtr c_self; TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); if (argc == 2) { beg = NUM2LONG(argv[0]); len = NUM2LONG(argv[1]); if (beg < 0) { beg += c_self->nodeNr ; } return subseq(rb_self, beg, len); } if (argc != 1) { rb_scan_args(argc, argv, "11", NULL, NULL); } arg = argv[0]; if (FIXNUM_P(arg)) { return index_at(rb_self, FIX2LONG(arg)); } /* if arg is Range */ switch (rb_range_beg_len(arg, &beg, &len, (long)c_self->nodeNr, 0)) { case Qfalse: break; case Qnil: return Qnil; default: return subseq(rb_self, beg, len); } return index_at(rb_self, NUM2LONG(arg)); }
Add the class attribute name
to all Node
objects in the NodeSet
.
See Nokogiri::XML::Node#add_class
for more information.
# File lib/nokogiri/xml/node_set.rb, line 139 def add_class(name) each do |el| el.add_class(name) end self end
Append the class attribute name
to all Node
objects in the NodeSet
.
See Nokogiri::XML::Node#append_class
for more information.
# File lib/nokogiri/xml/node_set.rb, line 151 def append_class(name) each do |el| el.append_class(name) end self end
Search this object for paths
, and return only the first result. paths
must be one or more XPath
or CSS
queries.
See Searchable#search
for more information.
Or, if passed an integer, index into the NodeSet:
node_set.at(3) # same as node_set[3]
Nokogiri::XML::Searchable#at
# File lib/nokogiri/xml/node_set.rb, line 119 def at(*args) if args.length == 1 && args.first.is_a?(Numeric) return self[args.first] end super(*args) end
Set attributes on each Node
in the NodeSet
, or get an attribute from the first Node
in the NodeSet
.
To get an attribute from the first Node
in a NodeSet:
node_set.attr("href") # => "https://www.nokogiri.org"
Note that an empty NodeSet
will return nil when #attr
is called as a getter.
To set an attribute on each node, key
can either be an attribute name, or a Hash of attribute names and values. When called as a setter, #attr
returns the NodeSet
.
If key
is an attribute name, then either value
or block
must be passed.
If key
is a Hash then attributes will be set for each key/value pair:
node_set.attr("href" => "https://www.nokogiri.org", "class" => "member")
If value
is passed, it will be used as the attribute value for all nodes:
node_set.attr("href", "https://www.nokogiri.org")
If block
is passed, it will be called on each Node
object in the NodeSet
and the return value used as the attribute value for that node:
node_set.attr("class") { |node| node.name }
# File lib/nokogiri/xml/node_set.rb, line 203 def attr(key, value = nil, &block) unless key.is_a?(Hash) || (key && (value || block)) return first&.attribute(key) end hash = key.is_a?(Hash) ? key : { key => value } hash.each do |k, v| each do |node| node[k] = v || yield(node) end end self end
Search this node set for CSS
rules
. rules
must be one or more CSS
selectors. For example:
For more information see Nokogiri::XML::Searchable#css
# File lib/nokogiri/xml/node_set.rb, line 83 def css(*args) rules, handler, ns, _ = extract_params(args) paths = css_rules_to_xpath(rules, ns) inject(NodeSet.new(document)) do |set, node| set + xpath_internal(node, paths, handler, ns, nil) end end
Returns the members of this NodeSet
as an array, to use in pattern matching.
⚡ This is an experimental feature, available since v1.14.0
# File lib/nokogiri/xml/node_set.rb, line 440 def deconstruct to_a end
Delete node
from the Nodeset, if it is a member. Returns the deleted node if found, otherwise returns nil.
static VALUE delete (VALUE rb_self, VALUE rb_node) { xmlNodeSetPtr c_self; xmlNodePtr node; Check_Node_Set_Node_Type(rb_node); TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); Noko_Node_Get_Struct(rb_node, xmlNode, node); if (xmlXPathNodeSetContains(c_self, node)) { xpath_node_set_del(c_self, node); return rb_node; } return Qnil ; }
Duplicate this NodeSet
. Note that the Nodes contained in the NodeSet
are not duplicated (similar to how Array and other Enumerable classes work).
static VALUE duplicate(VALUE rb_self) { xmlNodeSetPtr c_self; xmlNodeSetPtr dupl; TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); dupl = xmlXPathNodeSetMerge(NULL, c_self); return noko_xml_node_set_wrap(dupl, rb_iv_get(rb_self, "@document")); }
Iterate over each node, yielding to block
# File lib/nokogiri/xml/node_set.rb, line 231 def each return to_enum unless block_given? 0.upto(length - 1) do |x| yield self[x] end self end
Is this NodeSet
empty?
# File lib/nokogiri/xml/node_set.rb, line 45 def empty? length == 0 end
Filter this list for nodes that match expr
# File lib/nokogiri/xml/node_set.rb, line 130 def filter(expr) find_all { |node| node.matches?(expr) } end
Get the first element of the NodeSet
.
# File lib/nokogiri/xml/node_set.rb, line 29 def first(n = nil) return self[0] unless n list = [] [n, length].min.times { |i| list << self[i] } list end
Returns true if any member of node set equals node
.
static VALUE include_eh(VALUE rb_self, VALUE rb_node) { xmlNodeSetPtr c_self; xmlNodePtr node; Check_Node_Set_Node_Type(rb_node); TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); Noko_Node_Get_Struct(rb_node, xmlNode, node); return (xmlXPathNodeSetContains(c_self, node) ? Qtrue : Qfalse); }
Returns the index of the first node in self that is == to node
or meets the given block. Returns nil if no match is found.
# File lib/nokogiri/xml/node_set.rb, line 51 def index(node = nil) if node warn("given block not used") if block_given? each_with_index { |member, j| return j if member == node } elsif block_given? each_with_index { |member, j| return j if yield(member) } end nil end
Get the inner html of all contained Node
objects
# File lib/nokogiri/xml/node_set.rb, line 260 def inner_html(*args) collect { |j| j.inner_html(*args) }.join("") end
Get the inner text of all contained Node
objects
Note: This joins the text of all Node
objects in the NodeSet:
doc = Nokogiri::XML('<xml><a><d>foo</d><d>bar</d></a></xml>') doc.css('d').text # => "foobar"
Instead, if you want to return the text of all nodes in the NodeSet:
doc.css('d').map(&:text) # => ["foo", "bar"]
See Nokogiri::XML::Node#content
for more information.
# File lib/nokogiri/xml/node_set.rb, line 253 def inner_text collect(&:inner_text).join("") end
Return a nicely formated string representation
# File lib/nokogiri/xml/node_set.rb, line 427 def inspect "[#{map(&:inspect).join(", ")}]" end
Get the last element of the NodeSet
.
# File lib/nokogiri/xml/node_set.rb, line 39 def last self[-1] end
Get the length of the node set
static VALUE length(VALUE rb_self) { xmlNodeSetPtr c_self; TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); return c_self ? INT2NUM(c_self->nodeNr) : INT2NUM(0); }
Removes the last element from set and returns it, or nil
if the set is empty
# File lib/nokogiri/xml/node_set.rb, line 374 def pop return nil if length == 0 delete(last) end
Append node
to the NodeSet
.
static VALUE push(VALUE rb_self, VALUE rb_node) { xmlNodeSetPtr c_self; xmlNodePtr node; Check_Node_Set_Node_Type(rb_node); TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); Noko_Node_Get_Struct(rb_node, xmlNode, node); xmlXPathNodeSetAdd(c_self, node); return rb_self; }
Remove the attributed named name
from all Node
objects in the NodeSet
# File lib/nokogiri/xml/node_set.rb, line 223 def remove_attr(name) each { |el| el.delete(name) } self end
Remove the class attribute name
from all Node
objects in the NodeSet
.
See Nokogiri::XML::Node#remove_class
for more information.
# File lib/nokogiri/xml/node_set.rb, line 163 def remove_class(name = nil) each do |el| el.remove_class(name) end self end
Returns the first element of the NodeSet
and removes it. Returns nil
if the set is empty.
# File lib/nokogiri/xml/node_set.rb, line 383 def shift return nil if length == 0 delete(first) end
Element
reference - returns the node at index
, or returns a NodeSet
containing nodes starting at start
and continuing for length
elements, or returns a NodeSet
containing nodes specified by range
. Negative indices
count backward from the end of the node_set
(-1 is the last node). Returns nil if the index
(or start
) are out of range.
Return this list as an Array
static VALUE to_array(VALUE rb_self) { xmlNodeSetPtr c_self ; VALUE list; int i; TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); list = rb_ary_new2(c_self->nodeNr); for (i = 0; i < c_self->nodeNr; i++) { VALUE elt = noko_xml_node_wrap_node_set_result(c_self->nodeTab[i], rb_self); rb_ary_push(list, elt); } return list; }
# File lib/nokogiri/xml/node_set.rb, line 341 def to_html(*args) if Nokogiri.jruby? options = args.first.is_a?(Hash) ? args.shift : {} options[:save_with] ||= Node::SaveOptions::DEFAULT_HTML args.insert(0, options) end if empty? encoding = (args.first.is_a?(Hash) ? args.first[:encoding] : nil) encoding ||= document.encoding encoding.nil? ? "" : "".encode(encoding) else map { |x| x.to_html(*args) }.join end end
Convert this NodeSet
to a string.
# File lib/nokogiri/xml/node_set.rb, line 335 def to_s map(&:to_s).join end
Convert this NodeSet
to XHTML
# File lib/nokogiri/xml/node_set.rb, line 358 def to_xhtml(*args) map { |x| x.to_xhtml(*args) }.join end
Unlink this NodeSet
and all Node
objects it contains from their current context.
static VALUE unlink_nodeset(VALUE rb_self) { xmlNodeSetPtr c_self; int j, nodeNr ; TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); nodeNr = c_self->nodeNr ; for (j = 0 ; j < nodeNr ; j++) { if (! NOKOGIRI_NAMESPACE_EH(c_self->nodeTab[j])) { VALUE node ; xmlNodePtr node_ptr; node = noko_xml_node_wrap(Qnil, c_self->nodeTab[j]); rb_funcall(node, rb_intern("unlink"), 0); /* modifies the C struct out from under the object */ Noko_Node_Get_Struct(node, xmlNode, node_ptr); c_self->nodeTab[j] = node_ptr ; } } return rb_self ; }
Wrap each member of this NodeSet
with the node parsed from markup
or a dup of the node
.
- Parameters
-
markup (String) Markup that is parsed, once per member of the
NodeSet
, and used as the wrapper. Each node’s parent, if it exists, is used as the context node for parsing; otherwise the associated document is used. If the parsed fragment has multiple roots, the first root node is used as the wrapper. -
node (
Nokogiri::XML::Node
) An element that is ‘#dup`ed and used as the wrapper.
- Returns
-
self
, to support chaining.
⚠ Note that if a String
is passed, the markup will be parsed once per node in the NodeSet
. You can avoid this overhead in cases where you know exactly the wrapper you wish to use by passing a Node
instead.
Also see Node#wrap
Example with a String
argument:
doc = Nokogiri::HTML5(<<~HTML) <html><body> <a>a</a> <a>b</a> <a>c</a> <a>d</a> </body></html> HTML doc.css("a").wrap("<div></div>") doc.to_html # => <html><head></head><body> # <div><a>a</a></div> # <div><a>b</a></div> # <div><a>c</a></div> # <div><a>d</a></div> # </body></html>
Example with a Node
argument
💡 Note that this is faster than the equivalent call passing a String
because it avoids having to reparse the wrapper markup for each node.
doc = Nokogiri::HTML5(<<~HTML) <html><body> <a>a</a> <a>b</a> <a>c</a> <a>d</a> </body></html> HTML doc.css("a").wrap(doc.create_element("div")) doc.to_html # => <html><head></head><body> # <div><a>a</a></div> # <div><a>b</a></div> # <div><a>c</a></div> # <div><a>d</a></div> # </body></html>
# File lib/nokogiri/xml/node_set.rb, line 328 def wrap(node_or_tags) map { |node| node.wrap(node_or_tags) } self end
Search this node set for XPath
paths
. paths
must be one or more XPath
queries.
For more information see Nokogiri::XML::Searchable#xpath
# File lib/nokogiri/xml/node_set.rb, line 99 def xpath(*args) paths, handler, ns, binds = extract_params(args) inject(NodeSet.new(document)) do |set, node| set + xpath_internal(node, paths, handler, ns, binds) end end
Returns a new set built by merging the set and the elements of the given set.
static VALUE rb_xml_node_set_union(VALUE rb_self, VALUE rb_other) { xmlNodeSetPtr c_self, c_other; xmlNodeSetPtr c_new_node_set; if (!rb_obj_is_kind_of(rb_other, cNokogiriXmlNodeSet)) { rb_raise(rb_eArgError, "node_set must be a Nokogiri::XML::NodeSet"); } TypedData_Get_Struct(rb_self, xmlNodeSet, &xml_node_set_type, c_self); TypedData_Get_Struct(rb_other, xmlNodeSet, &xml_node_set_type, c_other); c_new_node_set = xmlXPathNodeSetMerge(NULL, c_self); c_new_node_set = xmlXPathNodeSetMerge(c_new_node_set, c_other); return noko_xml_node_set_wrap(c_new_node_set, rb_iv_get(rb_self, "@document")); }
Searching via XPath or CSS Queries
↑ topPublic Instance Methods
Search this node’s immediate children using CSS
selector selector
# File lib/nokogiri/xml/searchable.rb, line 201 def >(selector) # rubocop:disable Naming/BinaryOperatorParameterName ns = (document.root&.namespaces || {}) xpath(CSS.xpath_for(selector, prefix: "./", ns: ns).first) end
Search this object for CSS
rules
, and return only the first match. rules
must be one or more CSS
selectors.
See Searchable#css
for more information.
# File lib/nokogiri/xml/searchable.rb, line 143 def at_css(*args) css(*args).first end
Search this node for XPath
paths
, and return only the first match. paths
must be one or more XPath
queries.
See Searchable#xpath
for more information.
# File lib/nokogiri/xml/searchable.rb, line 193 def at_xpath(*args) xpath(*args).first end
Search this object for paths
. paths
must be one or more XPath
or CSS
queries:
node.search("div.employee", ".//title")
A hash of namespace bindings may be appended:
node.search('.//bike:tire', {'bike' => 'http://schwinn.com/'}) node.search('bike|tire', {'bike' => 'http://schwinn.com/'})
For XPath
queries, a hash of variable bindings may also be appended to the namespace bindings. For example:
node.search('.//address[@domestic=$value]', nil, {:value => 'Yes'})
💡 Custom XPath
functions and CSS
pseudo-selectors may also be defined. To define custom functions create a class and implement the function you want to define, which will be in the ‘nokogiri` namespace in XPath
queries.
The first argument to the method will be the current matching NodeSet
. Any other arguments are ones that you pass in. Note that this class may appear anywhere in the argument list. For example:
handler = Class.new { def regex node_set, regex node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ } end }.new node.search('.//title[nokogiri:regex(., "\w+")]', 'div.employee:regex("[0-9]+")', handler)
See Searchable#xpath
and Searchable#css
for further usage help.
# File lib/nokogiri/xml/searchable.rb, line 54 def search(*args) paths, handler, ns, binds = extract_params(args) xpaths = paths.map(&:to_s).map do |path| LOOKS_LIKE_XPATH.match?(path) ? path : xpath_query_from_css_rule(path, ns) end.flatten.uniq xpath(*(xpaths + [ns, handler, binds].compact)) end