Reputation: 245
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
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
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