How to Add Attribute to Nokogiri Node

How to add attribute to Nokogiri node?

I believe you should just need to use the []= method, i.e.

node['foo'] = 'bar'

You could also use node.set_attribute('foo', 'bar').

How to add an attribute to Nokogiri XML builder?

Add the attributes to the node like this

xml = Nokogiri::XML::Builder.new do |x|
x.root do
x.book(isbn: 1235) do
x.text('Don Quixot')
end
end
end.doc

Or, after re-rereading your question perhaps you wanted to add it to the parent further in the do block. In that case, this works:

xml = Nokogiri::XML::Builder.new do |x|
x.root do
x.book do
x.parent.set_attribute('isbn', 12345)
x.text('Don Quixot')
end
end
end.doc

Generates:

<?xml version="1.0"?>
<root>
<book isbn="1235">Don Quixot</book>
</root>

Setting an attribute in a Nokogiri::XML::NodeSet with css

The problem has little to do with CSS, since Nokogiri uses CSS selectors as an alternative to using XPath selectors, and both are only used to provide a path to a node, or nodes.

It looks like you're overthinking this, and making it harder than it needs to be. Here's what I'd do:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<xml>
<foo>
<Object>bar</Object>
</foo>
</xml>
EOT

doc.at('Object')["Id"] = Time.now.to_s

Looking at doc at this point shows:

puts doc.to_xml
# >> <?xml version="1.0"?>
# >> <xml>
# >> <foo>
# >> <Object Id="2014-01-28 19:13:32 -0700">bar</Object>
# >> </foo>
# >> </xml>

It's really important to understand the difference between at, at_css and at_xpath, which return the first matching Node, and search, css and xpath, which return NodeSets. A NodeSet is akin to an array containing Nodes. When you know that, your statement:

doc.css('Object').attr("Id").value = timestamp

won't make much sense, especially since that's not how the attr method is defined:

attr(key, value = nil, &blk) 

You'd need to use:

doc.css('Object').attr("Id", value)

which would assign value to all Id attributes for every <Object> node in the document.

But, again, that's not the right choice, instead you should use at or at_css to return the single node.

This was fine until the situation where 'Object' doesn't exist

If no <Object> node exists, then it gets more interesting, and you have to determine what to do. You can insert it, or, you can simply move along and do nothing.

To see if a node exists is simple:

object_node = doc.at('Object')
if object_node
object_node['Id'] = Time.now.to_s
else
# ... insert it
end

To insert a node involves locating the place you want to insert it, then add it:

doc.at('foo').add_child("<Object Id='#{ Time.now }'>baz</Object>")
puts doc.to_xml
# >> <?xml version="1.0"?>
# >> <xml>
# >> <foo>
# >> <Object>bar</Object>
# >> <Object Id="2014-01-28 19:39:38 -0700">baz</Object></foo>
# >> </xml>

I didn't try to make the XML output pretty, which isn't important in XML, it merely needs to be syntactically correct.

Also note that it's possible to insert a node, or nodes, by defining them as a string of XML. Nokogiri will parse it into the appropriate XML and graft it in where you said. You could also go the long route by define a NodeSet or Node, then inserting it, but, in general, that makes uglier code and causes you to do a lot more work, which results in less readable code for those who follow in your footsteps maintaining the source.

How to get the value of an attribute using Nokogiri

It's idiomatic to access parameter values by treating the node as a hash:

require 'nokogiri'

doc = Nokogiri::HTML('<div class="foo"></div>')
doc.at('div')['class'] # => "foo"

And, just like a hash, you can assign to it too:

doc.at('div')['class'] = 'bar'
puts doc.to_html

# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html><body><div class="bar"></div></body></html>

See [] and []= "Modifying Nodes and Attributes" in the documentation.

Ruby + Nokogiri: How to create XML node with attribute=value?

I would do as below :

require 'nokogiri'

builder = Nokogiri::XML::Builder.new do |xml|
xml.CityStateLookupRequest('userid' => 'xxxxxx' ) {
xml.zip("id" => '10'){
xml.Zip5 '90210'
}
}
end

puts builder.to_xml
# >> <?xml version="1.0"?>
# >> <CityStateLookupRequest userid="xxxxxx">
# >> <zip id="10">
# >> <Zip5>90210</Zip5>
# >> </zip>
# >> </CityStateLookupRequest>

How can I get nokogiri to select node attributes and add them to other nodes?

next_sibling should do the job

require 'rubygems'
require 'nokogiri'

frag = Nokogiri::XML(DATA)
frag.css('title').each { |t| t['id'] = "ID#{t.next_sibling.next_sibling['number']}" }
puts frag.to_xml

__END__
<root>
<title>Section X</title>
<paragraph number="1">Stuff</paragraph>
<title>Section Y</title>
<paragraph number="2">Stuff</paragraph>
</root>

Because whitespace is also a node, you have to call next_sibling twice. Maybe there is a way to avoid this.

Alternatively you can use an xpath expression to select the number attribute of the next paragraph

t['id'] = "ID#{t.xpath('following-sibling::paragraph/@number').first}"


Related Topics



Leave a reply



Submit