class REXML::Elements - rexml: Ruby Standard Library Documentation

# 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

new(parent) → new_elements_object click to toggle source

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 rexml-3.4.0/lib/rexml/element.rb, line 1604
    def initialize parent
      @element = parent
    end

### Public Instance Methods

<<(element=nil)
Alias for: add
elements[index] → element or nil click to toggle source
elements[xpath] → element or nil
elements[n, name] → element or nil

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 rexml-3.4.0/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

elements[] = index, replacement_element → replacement_element or nil click to toggle source

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 rexml-3.4.0/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

add → new_element click to toggle source
add(name) → new_element
add(element) → element

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 rexml-3.4.0/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

Also aliased as: <<
collect(xpath = nil) {|element| ... } → array click to toggle source

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 rexml-3.4.0/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

delete(index) → removed_element or nil click to toggle source
delete(element) → removed_element or nil
delete(xpath) → removed_element or nil

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 rexml-3.4.0/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

delete_all(xpath) click to toggle source

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 rexml-3.4.0/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

each(xpath = nil) {|element| ... } → self click to toggle source

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 rexml-3.4.0/lib/rexml/element.rb, line 1963
    def each( xpath=nil )
      XPath::each( @element, xpath ) {|e| yield e if e.kind_of? Element }
    end

empty? → true or false click to toggle source

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 rexml-3.4.0/lib/rexml/element.rb, line 1751
    def empty?
      @element.find{ |child| child.kind_of? Element}.nil?
    end

index(element) click to toggle source

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 rexml-3.4.0/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

inject(xpath = nil, initial = nil) → object click to toggle source

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 rexml-3.4.0/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

parent click to toggle source

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 rexml-3.4.0/lib/rexml/element.rb, line 1619
    def parent
      @element
    end

size → integer click to toggle source

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 rexml-3.4.0/lib/rexml/element.rb, line 2093
    def size
      count = 0
      @element.each {|child| count+=1 if child.kind_of? Element }
      count
    end

to_a(xpath = nil) → array_of_elements click to toggle source

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 rexml-3.4.0/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

### Private Instance Methods

literalize(name) click to toggle source

Private helper class. Removes quotes from quoted strings

    # File rexml-3.4.0/lib/rexml/element.rb, line 2125
    def literalize name
      name = name[1..-2] if name[0] == ?' or name[0] == ?"               #'
      name
    end