Nokogiri::XML::Node#dup showing unexpected result

Find the below code :

require 'nokogiri'

xml = <<-eot
<field property="dummyProperty"
depends="firstFieldEqualSecondFieldRequiredRelationship">
    <arg0 key="Country is Afghanistan" resource="false" />
    <arg1 key="Tax ID" resource="false" />
    <var>
        <var-name>firstOrField</var-name>
        <var-value>supplier.orgProfiles(${orgProfileId}).location.address.countryId</var-value>
    </var>
    <var>
        <var-name>firstFieldValue</var-name>
        <var-value>1487</var-value>
    </var>
</field>
eot

doc = Nokogiri::XML(xml)
node = doc.at('field')
new_node = node.dup
new_node.at('arg0')['key'] = "Country is Australlia"
new_node.at("//var[2]/var-value").content = 122

puts node

···

===========================
<field property="dummyProperty" depends="firstFieldEqualSeco
ionship">
<arg0 key="Country is Afghanistan" resource="false"/>
<arg1 key="Tax ID" resource="false"/>
<var>
  <var-name>firstOrField</var-name>
  <var-value>supplier.orgProfiles(${orgProfileId}).loc
yId</var-value>
</var>
<var>
  <var-name>firstFieldValue</var-name>
  <var-value>122</var-value>
</var>

puts new_node

<field property="dummyProperty"
depends="firstFieldEqualSecondFieldRequiredRelat
ionship">
    <arg0 key="Country is Australlia" resource="false"/>
    <arg1 key="Tax ID" resource="false"/>
    <var>
        <var-name>firstOrField</var-name>
        <var-value>supplier.orgProfiles(${orgProfileId}).location.address.countr
yId</var-value>
    </var>
    <var>
        <var-name>firstFieldValue</var-name>
        <var-value>1487</var-value>
    </var>
</field>

How in `node`, the tag `<var-value>` got changed to 122 from 1487. It is
not expected. That change should be happened in the `new_node`s
`<var-value>` tag.

What wrong I am doing here?

--
Posted via http://www.ruby-forum.com/.

#dup and #clone create shallow copies, i.e. all the references are
identical. So you copied only one node and not a sub tree.

Cheers

robert

···

On Mon, Jan 20, 2014 at 11:19 AM, Arup Rakshit <lists@ruby-forum.com> wrote:

doc = Nokogiri::XML(xml)
node = doc.at('field')
new_node = node.dup
new_node.at('arg0')['key'] = "Country is Australlia"
new_node.at("//var[2]/var-value").content = 122

What wrong I am doing here?

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

The below code worked:

require 'nokogiri'

xml = <<-eot
<field property="dummyProperty"
depends="firstFieldEqualSecondFieldRequiredRelationship">
    <arg0 key="Country is Afghanistan" resource="false" />
    <arg1 key="Tax ID" resource="false" />
    <var>
        <var-name>firstOrField</var-name>
        <var-value>supplier.orgProfiles(${orgProfileId}).location.address.countryId</var
-value>
    </var>
    <var>
        <var-name>firstFieldValue</var-name>
        <var-value>1487</var-value>
    </var>
</field>
eot

doc = Nokogiri::XML(xml)
node = doc.at('field')
dump_node = Marshal.load(Marshal.dump(node.to_s))
new_node = Nokogiri::XML(dump_node).at('field')
new_node.at('arg0')['key']="Country is Autralia"
new_node.at('//var[2]/var-value').content = 112
[new_node,node].each do |n|
  puts n.to_xml
  puts "-"*7
end
# >> <field property="dummyProperty"
depends="firstFieldEqualSecondFieldRequiredRelationship">
# >> <arg0 key="Country is Autralia" resource="false"/>
# >> <arg1 key="Tax ID" resource="false"/>
# >> <var>
# >> <var-name>firstOrField</var-name>
# >>
<var-value>supplier.orgProfiles(${orgProfileId}).location.address.countryId</var-value>-value&gt;
# >> </var>
# >> <var>
# >> <var-name>firstFieldValue</var-name>
# >> <var-value>112</var-value>
# >> </var>
# >> </field>
# >> -------
# >> <field property="dummyProperty"
depends="firstFieldEqualSecondFieldRequiredRelationship">
# >> <arg0 key="Country is Afghanistan" resource="false"/>
# >> <arg1 key="Tax ID" resource="false"/>
# >> <var>
# >> <var-name>firstOrField</var-name>
# >>
<var-value>supplier.orgProfiles(${orgProfileId}).location.address.countryId</var-value>-value&gt;
# >> </var>
# >> <var>
# >> <var-name>firstFieldValue</var-name>
# >> <var-value>1487</var-value>
# >> </var>
# >> </field>
# >> -------

···

--
Posted via http://www.ruby-forum.com/.

Robert Klemme wrote in post #1133716:

···

On Mon, Jan 20, 2014 at 11:19 AM, Arup Rakshit <lists@ruby-forum.com> > wrote:

doc = Nokogiri::XML(xml)
node = doc.at('field')

=====================================================
I copied the below node in totally
---------------------------------
<field property="dummyProperty"
depends="firstFieldEqualSecondFieldRequiredRelationship">
    <arg0 key="Country is Afghanistan" resource="false" />
    <arg1 key="Tax ID" resource="false" />
    <var>
        <var-name>firstOrField</var-name>
        <var-value>supplier.orgProfiles(${orgProfileId}).location.address.countryId</var
-value>
    </var>
    <var>
        <var-name>firstFieldValue</var-name>
        <var-value>1487</var-value>
    </var>
</field>
----------------------------------

Then I did a `#dup`. According to
http://nokogiri.org/Nokogiri/XML/Node.html#method-i-dup

**Copy this node. An optional depth may be passed in, but it defaults to
a deep copy. 0 is a shallow copy, 1 is a deep copy.**

It means `new_node = node.dup` I did here deep copy.

When I am doing the changes for one part changes happed in the `dup`
object

new_node.at('arg0')['key'] = "Country is Australlia"

But for the other part changes happedned in the original `node`, which I
expected to be happened in the `new_node` too.

new_node.at("//var[2]/var-value").content = 122

Why is that?

--
Posted via http://www.ruby-forum.com/\.

To make a deep copy, you need to pass 1 as argument.

node2 = node1.dup(1)

···

On Mon, Jan 20, 2014 at 11:21 AM, Arup Rakshit <lists@ruby-forum.com> wrote:

Robert Klemme wrote in post #1133716:
> On Mon, Jan 20, 2014 at 11:19 AM, Arup Rakshit <lists@ruby-forum.com> > > wrote:
>
>> doc = Nokogiri::XML(xml)
>> node = doc.at('field')

I copied the below node in totally
---------------------------------
<field property="dummyProperty"
depends="firstFieldEqualSecondFieldRequiredRelationship">
    <arg0 key="Country is Afghanistan" resource="false" />
    <arg1 key="Tax ID" resource="false" />
    <var>
        <var-name>firstOrField</var-name>

<var-value>supplier.orgProfiles(${orgProfileId}).location.address.countryId</var
-value>
    </var>
    <var>
        <var-name>firstFieldValue</var-name>
        <var-value>1487</var-value>
    </var>
</field>
----------------------------------

Then I did a `#dup`. According to
http://nokogiri.org/Nokogiri/XML/Node.html#method-i-dup

**Copy this node. An optional depth may be passed in, but it defaults to
a deep copy. 0 is a shallow copy, 1 is a deep copy.**

It means `new_node = node.dup` I did here deep copy.

When I am doing the changes for one part changes happed in the `dup`
object

new_node.at('arg0')['key'] = "Country is Australlia"

But for the other part changes happedned in the original `node`, which I
expected to be happened in the `new_node` too.

new_node.at("//var[2]/var-value").content = 122

Why is that?

--
Posted via http://www.ruby-forum.com/\.

--
Sadjow Medeiros Leão
Software Engineer

http://www.linkedin.com/pub/sadjow-leão/16/1/966
<http://www.rits.com.br>

Sadjow Leão wrote in post #1133732:

To make a deep copy, you need to pass 1 as argument.

node2 = node1.dup(1)

If you don't pass any argument, by default **deep copy** will be
happened.

···

--
Posted via http://www.ruby-forum.com/\.