How to add child nodes in NodeSet using Nokogiri
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
user1023627
Updated on June 17, 2022Comments
-
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 firstcatalogProductInfo
and I usedcollection.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 about 12 yearsNote that
<<
can also accept a string, so you can simply do:catalog << "<Book>abc</Book>"
-
Jamie about 12 yearsThanks for the tip @Phrogz, didn't know that.
-
user1023627 about 12 yearsThanks Phroz and Jamie that was really helpful. I will work on these and get back to you.
-
user1023627 about 12 yearshi phroz and jamie. i updated my question. could you please look at it.
-
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 about 12 yearsinstead 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 about 12 yearshi Phrogz and jamie .. could you please look at the my update on the question ?
-
user1023627 about 12 yearsHi @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 about 12 yearsHi @Jamie, Please help me out, I got stuck in here with OpenStruct and Nokogiri