raku-community-modules/XML

Iteratively using replace() on nodes gives unexpected results

Opened this issue · 3 comments

For example:

my @elements = $mydocument.root.nodes;
for @elements -> $element {
my $new-element = @new-elements.pop;
$mydocument.root.replace($element, $new-element);
}

seems to result in only the first and last $new-element ending up in the document. Sorry, I have not golfed this down further. I see a similar result with the replaceChild() syntax.

Also weird, when I look at the code for the other bug, I'll look into this one too.

Confirm dropping elements (...in 2024), not sure if my syntax is valid. Still, replacing nodes with self drops 1/2 the elements:

~$ raku -MXML -e 'my $mydocument=open-xml($*ARGFILES.Str);  \
                  my @elements = $mydocument.root.nodes;       \
                  my @new-elements = $mydocument.root.nodes;      \
                  @elements.say; say "\n____\n"; @new-elements.say; say "\n____\n";  \
                  for @elements -> $element {    \
                      my $new-element = @new-elements.pop;    \
                      $mydocument.root.replace($element, $new-element);  \
                  }; .say for $mydocument;' ~/exemel_text.xml

[
   <file>text1</file>
   <file>text2</file>
   <file>text3</file>
   <file>text4</file>
]

____

[
   <file>text1</file>
   <file>text2</file>
   <file>text3</file>
   <file>text4</file>
]

____

<?xml version="1.0"?><root>
  <file>text1</file>
  <file>text2</file>
  </root>
~$

Try reversing the @new-elements replacement, now all 4 elements are replaced with blanks! (Also...weirdness with printing reversed array):

~$ raku -MXML -e 'my $mydocument=open-xml($*ARGFILES.Str);  \
                  my @elements = $mydocument.root.nodes;       \
                  my @new-elements = $mydocument.root.nodes.reverse;    \  
                  @elements.say; say "\n____\n"; @new-elements.say; say "\n____\n";  \
                  for @elements -> $element {    \
                      my $new-element = @new-elements.pop;    \
                      $mydocument.root.replace($element, $new-element);     \
                  }; .say for $mydocument;' ~/exemel_text.xml

[
   <file>text1</file>
   <file>text2</file>
   <file>text3</file>
   <file>text4</file>
]

____

[
 <file>text4</file>
   <file>text3</file>
   <file>text2</file>
   <file>text1</file>
  ]

____

<?xml version="1.0"?><root>




</root>
~$ 

Different syntax but still errors: "No such method 'replace' for invocant of type 'Array'"

 ~$ raku -MXML -e 'my $mydocument=open-xml($*ARGFILES.Str);     \
                   my @elements = $mydocument.root.nodes;          \
                   my @new-elements = $mydocument.root.nodes.reverse;     \
                   for @(@elements,@new-elements) -> $old,$new {   \
                       $mydocument.root.nodes.replace($old, $new);     \
                   };  .say for $mydocument;' ~/exemel_text.xml
No such method 'replace' for invocant of type 'Array'
  in block <unit> at -e line 1

Maybe related to #69 ?

@supernovus
@bronco-creek
@jonathanstowe
@lizmat
@dwarring
@vrurg

Here's a working one-liner for replacing text within user identified nodes ( restricted to top-level with :RECURSE(0) and restricted to :TAG{"title"}):

~$ raku -MXML -e 'my  $xml = open-xml( $*ARGFILES.Str );
                  for $xml.elements( :RECURSE(0), :TAG{"title"} ) -> $E {
                      my $old = $E.contents[0];
                      my $new = XML::Text.new( text => $old.text.subst(/^old-text$/, "new-text" ) );
                      $E.replace( $old, $new );
                  };  .say for $xml;'   file.xml

See: https://unix.stackexchange.com/a/771574/227738

Maybe add a warning to the XML docs page about trying to replace Elements without converting to XML::Element first?