fassen
fassen

Reputation: 245

xmlstarlet add element with namespace and attributes

I'm trying to add a node with a namespace and an attribute to an xml, but it fails if I try to do it as multiple commands in one execution of xmlstarlet:

<?xml version="1.0"?>
<levela xmlns:xi="http://www.w3.org/2001/XInclude">
  <levelb>
  </levelb>
</levela>

xmlstarlet ed -L -s /levela/levelb -t elem -n xi:input -i //xi:input -t attr -n "href" -v "aHref" file.xml

I'm trying to get:

<?xml version="1.0"?>
<levela xmlns:xi="http://www.w3.org/2001/XInclude">
  <levelb>
     <xi:input href="aHref"/>
  </levelb>
</levela>

But the attribute isn't added. So I get:

<?xml version="1.0"?>
<levela xmlns:xi="http://www.w3.org/2001/XInclude">
  <levelb>
     <xi:input/>
  </levelb>
</levela>

It works if I run it as two executions like this:

xmlstarlet ed -L -s /levela/levelb -t elem -n xi:input file.xml

xmlstarlet ed -L -i //xi:input -t attr -n "href" -v "aHref" file.xml

It also works if I add a tag without a namespace e.g:

xmlstarlet ed -L -s /levela/levelb -t elem -n levelc -i //levelc -t attr -n "href" -v "aHref" file.xml

<?xml version="1.0"?>
<levela xmlns:xi="http://www.w3.org/2001/XInclude">
  <levelb>
     <levelc href="aHref"/>
  </levelb>
</levela>

What am I doing wrong? Why doesn't it work with the namespace?

Upvotes: 2

Views: 798

Answers (2)

urznow
urznow

Reputation: 1801

This will do it:

xmlstarlet edit \
  -s '/levela/levelb' -t elem -n 'xi:input' \
  -s '$prev' -t attr -n 'href' -v 'aHref' \
file.xml

xmlstarlet edit code can use the convenience $prev (aka $xstar:prev) variable to refer to the node created by the most recent -i (--insert), -a (--append), or -s (--subnode) option. Examples of $prev are given in doc/xmlstarlet.txt and the source code's examples/ed-backref*.

Attributes can be added using -i, -a, or -s.

What am I doing wrong? Why doesn't it work with the namespace?

Update 2022-04-15
The -i '//xi:input' … syntax you use is perfectly logical. As your own 2 alternative commands suggest it's the namespace xi that triggers the omission and there's a hint in the edInsert function in the source code's src/xml_edit.c where it says NULL /* TODO: NS */. When you've worked with xmlstarlet for some time you come to accept its limitations (or not); in this case the $prev back reference is useful. I wouldn't expect that TODO to go away anytime soon.
(end update)

Well, I think xmlstarlet edit looks upon node naming as a user responsibility, as the following example suggests,

printf '<v/>' |
xmlstarlet edit --omit-decl \
  -s '*' -t elem -n 'undeclared:qname' -v 'x' \
  -s '*' -t elem -n '!--' -v ' wotsinaname ' \
  -s '$prev' -t attr -n ' "" ' -v '' \
  -s '*' -t elem -n ' <&> ' -v 'harrumph!' 

the output of which is clearly not XML:

<v>
  <undeclared:qname>x</undeclared:qname>
  <!--  "" =""> wotsinaname </!-->
  < <&> >harrumph!</ <&> >
</v>

If you want to indent the new element, for example:

xmlstarlet edit \
  -s '/levela/levelb' -t elem -n 'xi:input' \
  --var newnd '$prev' \
  -s '$prev' -t attr -n 'href' -v 'aHref' \
  -a '$newnd' -t text -n ignored -v '' \
  -u '$prev' -x '(//text())[1][normalize-space()=""]' \
file.xml

The -x XPath expression grabs the first text node provided it contains nothing but whitespace, i.e. the first child node of levela. The --var name xpath option to define an xmlstarlet edit variable is mentioned in doc/xmlstarlet.txt but not in the user's guide.

I used xmlstarlet version 1.6.1.

Upvotes: 1

Jack Fleeting
Jack Fleeting

Reputation: 24930

It seems you can't insert an attribute and attribute value into a namespaced node... Maybe someone smarter can figure out something else, but the only way I could get around that, at least in this case, is this:

 xmlstarlet ed -N xi="http://www.w3.org/2001/XInclude" --subnode "//levela/levelb" \
 --type elem -n "xi:input" --insert  "//levela/levelb/*"  --type attr --name "href"\
 --value "aHref" file.xml

Upvotes: 0

Related Questions