How to add child nodes in NodeSet using Nokogiri

15,677

If I am understanding you correctly, the following should be roughly what you are looking for:

doc = Nokogiri::XML(original_xml_string) 

catalog = doc.at_css('Catalog') # #at_css will just grab the first node.
                                # use #css if you want to loop through several.
                                # alternatively just use doc.root

book = Nokogiri::XML::Node.new('Book', doc)
book_author = Nokogiri::XML::Node.new('Book_Author', doc)

book.content = 'abc'
book_author.content  = 'benjamin'

catalog << book
catalog << book_author

The << should append the nodes just before the end of the element.

After the updated question and simplified with @Phrogz's suggestions, this should meet your requirements:

require 'nokogiri'

xml = <<'XML'
<Catalog>
  <Interface></Interface>
  <Dialog></Dialog>
  <Manifest></Manifest>
</Catalog>
XML

doc = Nokogiri::XML(xml) 
catalog = doc.root

catalog.first_element_child.before("<Book_Author>abc</Book_Author>")
catalog.first_element_child.before("<Book>benjamin</Book>")

puts doc.to_xml

To iterate over a collection, add the nodes dynamically, and using a NodeSet, try the following:

require 'nokogiri'
require 'ostruct'

xml = <<-'XML'
<Catalog>
  <Interface></Interface>
  <Dialog></Dialog>
  <Manifest></Manifest>
</Catalog>
XML

collection = [
  OpenStruct.new(book: '1984', pen: 'George Orwell'),
  OpenStruct.new(book: 'Thinking, Fash and Slow', pen: 'Daniel Kahneman')
]

doc = Nokogiri::XML(xml) 
catalog = doc.root

node_set = Nokogiri::XML::NodeSet.new(doc)
collection.each do |object|
  book = Nokogiri::XML::Node.new('Book', doc)
  book_author = Nokogiri::XML::Node.new('Book_Author', doc)

  book.content = object.book
  book_author.content = object.pen

  node_set << book
  node_set << book_author
end

catalog.first_element_child.before(node_set)

puts doc.to_xml
Share:
15,677
user1023627
Author by

user1023627

Updated on June 17, 2022

Comments

  • user1023627
    user1023627 about 2 years

    I am trying to add child nodes under a root node. I tried it with the following XML but it doesn't work.

    builder = Nokogiri::XML::Builder.with(@doc) do |xml|   
      nodes = Nokogiri::XML::NodeSet.new(@doc, [])   
      [].each {|nodes_one_by_one|  
        << nodes_one_by_one.Book  
        << nodes_one_by_one.Pen 
      }      
    end  
    

    I need to add nodes below a root node like this:

    <Catalog>
        <Book>abc</Book>
        <Book_Author>Benjamin</Book_author>
    </Catalog>
    

    That works for me, but I to add these Nodes after a specific position in the document:

    <Catalog>
        <!-- 
        <Book>abc</Book>
        <Book_Author>Benjamin</Book_author>
        -->
        <Interface></Interface>
        <Dialog></Dialog>
        <Manifest></Manifest>
    </Catalog>
    

    I tried it with at_xpath('//Catlog') but it is adding it at the end of the element:

     <Catalog>
        <Interface></Interface>
        <Dialog></Dialog>
        <Manifest></Manifest>
         <!-- 
          <Book>abc</Book>
          <Book_Author>Benjamin</Book_author>
         -->
     </Catalog>
    

    and

    book = Nokogiri::XML::Node.new('book', doc)
    pen  = Nokogiri::XML::Node.new('pen', doc)     
    .
    .
    

    Is there a way to loop using each instead of adding them one by one. I tried this way but that doesn't work:

    builder = Nokogiri::XML::Builder.with(doc) do |xml|   
        nodes = Nokogiri::XML::Node.new(doc, [])   
        [].each {|child_list_element|  
            child_list_element.Book  "value"
            child_list_element.Pen  "value" 
            child_list_element.Diary  "value"
            child_list_element.Pen_stand  "value"
            child_list_element.Pencil  "value"
            .
            .
            .        
    }      
    end  
    doc << nodes
    

    The code might be wrong, but I want to do this way.

    Also, can I add all the elements as a NodeSet instead of a Node.


    Nested OpenStruct doesn't seem to be working. I tried:

    Catalog collection of Store:

        require 'ostruct'
        require 'nokogiri'
    
        collection = [
        OpenStruct.new(:catalogStoreNumber => '657758',
        :catalogStoreId => 'CTH6536',
        :catalogStoreLocation => 'UnitedStates', 
        :catalogOwnerId => 'TYCT11190',
        :catalogOwner => 'McGrawHill Pub.',
        :catalogList => OpenStruct.new(
            :catalogProductInfo => OpenStruct.new(
          :productType => 'Book',
          :productName => 'The Client',
          :productId => 'CRSUS113246A',
          :productCategory => 'Crime And Suspense',
          :productSubcategory => 'Vintage Books',  
          :productPrice => '45.50 USD',
          :productAuthor => OpenStruct.new(
                    :authorFirstName =>'John Grisham',
        :authorMiddleName=> 'Willburt',
        :authorContact => '19876648981')),
      :catalogProductInfo => OpenStruct.new(
          :productType => 'Pen',
          :productName => 'Reynolds',
          :productId => 'PRREY546647',
          :productCategory => 'Misc. Stationary',
          :productSubcategory => 'Stationery Items',  
          :productPrice => '3.00 USD',
          :productManufacturer => 'Reynolds Inc.',
          :productAuthor => OpenStruct.new(
              :authorFirstName => 'Ryan Reynolds',
        :authorMiddleName => 'William',
        :authorContact => '16577589898')),
        :catalogListType => 'ProductCollection',
        :catalogListSource => 'Web'
        ),
        :catalogVerificaitionLog => OpenStruct.new(
            :catalogVerificationStatus => 'Verified',
      :catalogVerificationDateTime => '2012-03-12T13:00:15+5:30',
      :catalogVerificationId => '64774A',
      :catalogVerificationRequestedBy => 'user_121')
        )]
    

    I want to access the productType of the first catalogProductInfo and I used

    collection.catalogList.catalogProductInfo.productType.content
    

    and I got this error:

    undefined method `productType' for #<Array:0x3057438> (NoMethodError)
    

    Does OpenStruct have the support for the nested OpenStruct I want to construct XML in the following format using OpenStruct and Nokogiri?

    <CatalogOrder>
      <CatalogStoreNumber>657758</CatalogStoreNumber>
      <CatalogStoreId>CTH6536</CatalogStoreId>
      <CatalogStoreLocation>UnitedStates</CatalogStoreLocation>
      <CatalogOwnerId>TYCT11190</CatalogOwnerId>
      <CatalogOwner>McGrawHill Pub.</CatalogOwner>
      <CatalogList>
        <CatalogProductInfo>
          <ProductType>Book</ProductType>
          <ProductName>The Client</ProductName>                
          <ProductId>CRSUS113246A</ProductId>
          <ProductCategory>Crime And Suspense</ProductCategory>
          <ProductSubCategory>Vintage Books</ProductSubCategory>
          <ProductPrice>45.50 USD</ProductPrice>
          <ProductAuthor>
            <AuthorFirstName>John Grisham</AuthorFirstName>
            <AuthorMiddleName>Willbur</AuthorMiddleName>
            <AuthorContact>19876648981</AuthorContact>
          </ProductAuthor>
        </CatalogProductInfo>
        <CatalogProductInfo>
          <ProductType>Pen</ProductType>
          <ProductName>Reynolds</ProductName>                
          <ProductId>PRREY546647</ProductId>
          <ProductCategory>Misc. Stationary</ProductCategory>
          <ProductSubCategory>Stationary Items</ProductSubCategory>
          <ProductPrice>3.00 USD</ProductPrice>
          <ProductAuthor>
            <AuthorFirstName>Ryan Reynolds</AuthorFirstName>
            <AuthorMiddleName>William</AuthorMiddleName>
            <AuthorContact>16577589898</AuthorContact>
          </ProductAuthor>
        </CatalogProductInfo>
        <CatalogListType>ProductCollection</CatalogListType>
        <CatalogListSource>Web</CatalogListSource>
      </CatalogList>
      <CatalogVerificationLog>
        <CatalogVerificationStatus>Verified</CatalogVerificationStatus>
        <CatalogVerificationDateTime>2012-03-12T13:00:15+5:30</CatalogVerificationDateTime>
        <CatalogVerificationId>64774A</CatalogVerificationId>
        <CatalogVerificationRequestedBy>User_121</CatalogVerificationRequestedBy>
      </CatalogVerificationLog>
    </CatalogOrder>
    

    I want to do this using Nokogiri and OpenStruct, but I am not sure whether it is possible with OpenStruct, as it lacks nesting capabilities. Is there any other way to use JSON to accomplish this without any limitations?

  • Phrogz
    Phrogz about 12 years
    Note that << can also accept a string, so you can simply do: catalog << "<Book>abc</Book>"
  • Jamie
    Jamie about 12 years
    Thanks for the tip @Phrogz, didn't know that.
  • user1023627
    user1023627 about 12 years
    Thanks Phroz and Jamie that was really helpful. I will work on these and get back to you.
  • user1023627
    user1023627 about 12 years
    hi phroz and jamie. i updated my question. could you please look at it.
  • Phrogz
    Phrogz about 12 years
    @user1023627 To insert a node as the new first child of an element insert it before the existing first child: catalog.first_element_child.before("<Book>abc</Book>").
  • user1023627
    user1023627 about 12 years
    instead adding each and every element using .. book = Nokogiri::XML::Node.new('book', doc) pen = Nokogiri::XML::Node.new('pen', doc) .. can i use each.do and add elements using a loop and append them once at end eg:- doc << nodes
  • user1023627
    user1023627 about 12 years
    hi Phrogz and jamie .. could you please look at the my update on the question ?
  • user1023627
    user1023627 about 12 years
    Hi @Phrogz, ! The Nested OpenStruct doesn't seem to be working fine. I've below mentioned requirement, which isn't possible with the above mentioned solution. Could you please send me any corrections, in the above mentioned code. I also wanted to know which way storing the data is most scalable(I don't want to use YAML anymore bcoz i don't want multiple ".yml" files occupying my workspace for each and every specific scenario). Is there any way I can use JSon to store the data and Construct XML using JSon files.(If yes, give me the tips to store data in ".json" file and construct using Nokogiri
  • user1023627
    user1023627 about 12 years
    Hi @Jamie, Please help me out, I got stuck in here with OpenStruct and Nokogiri