class REXML::Elements
A class which provides filtering of children for Elements
, and XPath
search support. You are expected to only encounter this class as the element.elements
object. Therefore, you are not expected to instantiate this yourself.
xml_string = <<-EOT <?xml version="1.0" encoding="UTF-8"?> <bookstore> <book category="cooking"> <title lang="en">Everyday Italian</title> <author>Giada De Laurentiis</author> <year>2005</year> <price>30.00</price> </book> <book category="children"> <title lang="en">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="web"> <title lang="en">XQuery Kick Start</title> <author>James McGovern</author> <author>Per Bothner</author> <author>Kurt Cagle</author> <author>James Linn</author> <author>Vaidyanathan Nagarajan</author> <year>2003</year> <price>49.99</price> </book> <book category="web" cover="paperback"> <title lang="en">Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book> </bookstore> EOT d = REXML::Document.new(xml_string) elements = d.root.elements elements # => #<REXML::Elements @element=<bookstore> ... </>>
Public Class Methods
Returns a new Elements object with the given parent
. Does not assign parent.elements = self
:
d = REXML::Document.new(xml_string) eles = REXML::Elements.new(d.root) eles # => #<REXML::Elements @element=<bookstore> ... </>> eles == d.root.elements # => false
# File lib/rexml/element.rb, line 1604 def initialize parent @element = parent end
Public Instance Methods
Returns the first Element object selected by the arguments, if any found, or nil
if none found.
Notes:
-
The
index
is 1-based, not 0-based, so that:-
The first element has index
1
-
The nth element has index
n
.
-
-
The selection ignores non-Element nodes.
When the single argument index
is given, returns the element given by the index, if any; otherwise, nil
:
d = REXML::Document.new(xml_string) eles = d.root.elements eles # => #<REXML::Elements @element=<bookstore> ... </>> eles[1] # => <book category='cooking'> ... </> eles.size # => 4 eles[4] # => <book category='web' cover='paperback'> ... </> eles[5] # => nil
The node at this index is not an Element, and so is not returned:
eles = d.root.first.first # => <title lang='en'> ... </> eles.to_a # => ["Everyday Italian"] eles[1] # => nil
When the single argument xpath
is given, returns the first element found via that xpath
, if any; otherwise, nil
:
eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>> eles['/bookstore'] # => <bookstore> ... </> eles['//book'] # => <book category='cooking'> ... </> eles['//book [@category="children"]'] # => <book category='children'> ... </> eles['/nosuch'] # => nil eles['//nosuch'] # => nil eles['//book [@category="nosuch"]'] # => nil eles['.'] # => <bookstore> ... </> eles['..'].class # => REXML::Document
With arguments n
and name
given, returns the nth found element that has the given name
, or nil
if there is no such nth element:
eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>> eles[1, 'book'] # => <book category='cooking'> ... </> eles[4, 'book'] # => <book category='web' cover='paperback'> ... </> eles[5, 'book'] # => nil
# File lib/rexml/element.rb, line 1676 def []( index, name=nil) if index.kind_of? Integer raise "index (#{index}) must be >= 1" if index < 1 name = literalize(name) if name num = 0 @element.find { |child| child.kind_of? Element and (name.nil? ? true : child.has_name?( name )) and (num += 1) == index } else return XPath::first( @element, index ) #{ |element| # return element if element.kind_of? Element #} #return nil end end
Replaces or adds an element.
When eles[index]
exists, replaces it with replacement_element
and returns replacement_element
:
d = REXML::Document.new(xml_string) eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>> eles[1] # => <book category='cooking'> ... </> eles[1] = REXML::Element.new('foo') eles[1] # => <foo/>
Does nothing (or raises an exception) if replacement_element
is not an Element:
eles[2] # => <book category='web' cover='paperback'> ... </> eles[2] = REXML::Text.new('bar') eles[2] # => <book category='web' cover='paperback'> ... </>
When eles[index]
does not exist, adds replacement_element
to the element and returns
d = REXML::Document.new(xml_string) eles = d.root.elements # => #<REXML::Elements @element=<bookstore> ... </>> eles.size # => 4 eles[50] = REXML::Element.new('foo') # => <foo/> eles.size # => 5 eles[5] # => <foo/>
Does nothing (or raises an exception) if replacement_element
is not an Element:
eles[50] = REXML::Text.new('bar') # => "bar" eles.size # => 5
# File lib/rexml/element.rb, line 1731 def []=( index, element ) previous = self[index] if previous.nil? @element.add element else previous.replace_with element end return previous end
Adds an element; returns the element added.
With no argument, creates and adds a new element. The new element has:
-
No name.
-
Parent from the Elements object.
-
Context from the that parent.
Example:
d = REXML::Document.new(xml_string) elements = d.root.elements parent = elements.parent # => <bookstore> ... </> parent.context = {raw: :all} elements.size # => 4 new_element = elements.add # => </> elements.size # => 5 new_element.name # => nil new_element.parent # => <bookstore> ... </> new_element.context # => {:raw=>:all}
With string argument name
, creates and adds a new element. The new element has:
-
Name
name
. -
Parent from the Elements object.
-
Context from the that parent.
Example:
d = REXML::Document.new(xml_string) elements = d.root.elements parent = elements.parent # => <bookstore> ... </> parent.context = {raw: :all} elements.size # => 4 new_element = elements.add('foo') # => <foo/> elements.size # => 5 new_element.name # => "foo" new_element.parent # => <bookstore> ... </> new_element.context # => {:raw=>:all}
With argument element
, creates and adds a clone of the given element
. The new element has name, parent, and context from the given element
.
d = REXML::Document.new(xml_string) elements = d.root.elements elements.size # => 4 e0 = REXML::Element.new('foo') e1 = REXML::Element.new('bar', e0, {raw: :all}) element = elements.add(e1) # => <bar/> elements.size # => 5 element.name # => "bar" element.parent # => <bookstore> ... </> element.context # => {:raw=>:all}
# File lib/rexml/element.rb, line 1921 def add element=nil if element.nil? Element.new("", self, @element.context) elsif not element.kind_of?(Element) Element.new(element, self, @element.context) else @element << element element.context = @element.context element end end
Iterates over the elements; returns the array of block return values.
With no argument, iterates over all elements:
d = REXML::Document.new(xml_string) elements = d.root.elements elements.collect {|element| element.size } # => [9, 9, 17, 9]
With argument xpath
, iterates over elements that match the given xpath
:
xpath = '//book [@category="web"]' elements.collect(xpath) {|element| element.size } # => [17, 9]
# File lib/rexml/element.rb, line 1984 def collect( xpath=nil ) collection = [] XPath::each( @element, xpath ) {|e| collection << yield(e) if e.kind_of?(Element) } collection end
Removes an element; returns the removed element, or nil
if none removed.
With integer argument index
given, removes the child element at that offset:
d = REXML::Document.new(xml_string) elements = d.root.elements elements.size # => 4 elements[2] # => <book category='children'> ... </> elements.delete(2) # => <book category='children'> ... </> elements.size # => 3 elements[2] # => <book category='web'> ... </> elements.delete(50) # => nil
With element argument element
given, removes that child element:
d = REXML::Document.new(xml_string) elements = d.root.elements ele_1, ele_2, ele_3, ele_4 = *elements elements.size # => 4 elements[2] # => <book category='children'> ... </> elements.delete(ele_2) # => <book category='children'> ... </> elements.size # => 3 elements[2] # => <book category='web'> ... </> elements.delete(ele_2) # => nil
With string argument xpath
given, removes the first element found via that xpath:
d = REXML::Document.new(xml_string) elements = d.root.elements elements.delete('//book') # => <book category='cooking'> ... </> elements.delete('//book [@category="children"]') # => <book category='children'> ... </> elements.delete('//nosuch') # => nil
# File lib/rexml/element.rb, line 1821 def delete element if element.kind_of? Element @element.delete element else el = self[element] el.remove if el end end
Removes all elements found via the given xpath
; returns the array of removed elements, if any, else nil
.
d = REXML::Document.new(xml_string) elements = d.root.elements elements.size # => 4 deleted_elements = elements.delete_all('//book [@category="web"]') deleted_elements.size # => 2 elements.size # => 2 deleted_elements = elements.delete_all('//book') deleted_elements.size # => 2 elements.size # => 0 elements.delete_all('//book') # => []
# File lib/rexml/element.rb, line 1847 def delete_all( xpath ) rv = [] XPath::each( @element, xpath) {|element| rv << element if element.kind_of? Element } rv.each do |element| @element.delete element element.remove end return rv end
Iterates over the elements.
With no argument, calls the block with each element:
d = REXML::Document.new(xml_string) elements = d.root.elements elements.each {|element| p element }
Output:
<book category='cooking'> ... </> <book category='children'> ... </> <book category='web'> ... </> <book category='web' cover='paperback'> ... </>
With argument xpath
, calls the block with each element that matches the given xpath
:
elements.each('//book [@category="web"]') {|element| p element }
Output:
<book category='web'> ... </> <book category='web' cover='paperback'> ... </>
# File lib/rexml/element.rb, line 1963 def each( xpath=nil ) XPath::each( @element, xpath ) {|e| yield e if e.kind_of? Element } end
Returns true
if there are no children, false
otherwise.
d = REXML::Document.new('') d.elements.empty? # => true d = REXML::Document.new(xml_string) d.elements.empty? # => false
# File lib/rexml/element.rb, line 1751 def empty? @element.find{ |child| child.kind_of? Element}.nil? end
Returns the 1-based index of the given element
, if found; otherwise, returns -1:
d = REXML::Document.new(xml_string) elements = d.root.elements ele_1, ele_2, ele_3, ele_4 = *elements elements.index(ele_4) # => 4 elements.delete(ele_3) elements.index(ele_4) # => 3 elements.index(ele_3) # => -1
# File lib/rexml/element.rb, line 1769 def index element rv = 0 found = @element.find do |child| child.kind_of? Element and (rv += 1) and child == element end return rv if found == element return -1 end
Calls the block with elements; returns the last block return value.
With no argument, iterates over the elements, calling the block elements.size - 1
times.
-
The first call passes the first and second elements.
-
The second call passes the first block return value and the third element.
-
The third call passes the second block return value and the fourth element.
-
And so on.
In this example, the block returns the passed element, which is then the object argument to the next call:
d = REXML::Document.new(xml_string) elements = d.root.elements elements.inject do |object, element| p [elements.index(object), elements.index(element)] element end
Output:
[1, 2] [2, 3] [3, 4]
With the single argument xpath
, calls the block only with elements matching that xpath:
elements.inject('//book [@category="web"]') do |object, element| p [elements.index(object), elements.index(element)] element end
Output:
[3, 4]
With argument xpath
given as nil
and argument initial
also given, calls the block once for each element.
-
The first call passes the
initial
and the first element. -
The second call passes the first block return value and the second element.
-
The third call passes the second block return value and the third element.
-
And so on.
In this example, the first object index is -1
elements.inject(nil, 'Initial') do |object, element| p [elements.index(object), elements.index(element)] element end
Output:
[-1, 1] [1, 2] [2, 3] [3, 4]
In this form the passed object can be used as an accumulator:
elements.inject(nil, 0) do |total, element| total += element.size end # => 44
With both arguments xpath
and initial
are given, calls the block only with elements matching that xpath:
elements.inject('//book [@category="web"]', 0) do |total, element| total += element.size end # => 26
# File lib/rexml/element.rb, line 2069 def inject( xpath=nil, initial=nil ) first = true XPath::each( @element, xpath ) {|e| if (e.kind_of? Element) if (first and initial == nil) initial = e first = false else initial = yield( initial, e ) if e.kind_of? Element end end } initial end
Returns the parent element cited in creating the Elements object. This element is also the default starting point for searching in the Elements object.
d = REXML::Document.new(xml_string) elements = REXML::Elements.new(d.root) elements.parent == d.root # => true
# File lib/rexml/element.rb, line 1619 def parent @element end
Returns the count of Element children:
d = REXML::Document.new '<a>sean<b/>elliott<b/>russell<b/></a>' d.root.elements.size # => 3 # Three elements. d.root.size # => 6 # Three elements plus three text nodes..
# File lib/rexml/element.rb, line 2093 def size count = 0 @element.each {|child| count+=1 if child.kind_of? Element } count end
Returns an array of element children (not including non-element children).
With no argument, returns an array of all element children:
d = REXML::Document.new '<a>sean<b/>elliott<c/></a>' elements = d.root.elements elements.to_a # => [<b/>, <c/>] # Omits non-element children. children = d.root.children children # => ["sean", <b/>, "elliott", <c/>] # Includes non-element children.
With argument xpath
, returns an array of element children that match the xpath:
elements.to_a('//c') # => [<c/>]
# File lib/rexml/element.rb, line 2117 def to_a( xpath=nil ) rv = XPath.match( @element, xpath ) return rv.find_all{|e| e.kind_of? Element} if xpath rv end