1

I have a SVG file, which is an XML document:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
  <defs
     id="defs2">
  <!-- a lot of stuff> </defs>
  <!-- more stuff-->
</svg>

I want to add the svg: prefix to all the tags corresponding to the default namespace in order to get next output:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg:svg
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
  <svg:defs
     id="defs2">
  <!-- a lot of stuff> </svg:defs>
  <!-- more stuff-->
</svg:svg>

I am sure this is possible in one or few command lines from the shell using xmllint and/or xmlstarlet but I can't manage it.

2 Answers 2

2

It's been a while since you asked. Nevertheless ...

xmlstarlet edit's -r / --rename action requires a literal value for the new name so XPath functions are out. However, xmlstarlet select can be used as a code generator to produce the edit command:

xmlstarlet select -t \
  --var sq -o "'" -b \
  -o 'xmlstarlet edit --pf \' -n \
  -m 'set:distinct(//_:*)' \
    -o '  -r ' -v 'concat($sq,"//_:",local-name(),$sq)' \
    -o '  -v ' -v 'concat($sq,"svg:",local-name(),$sq)' -o ' \' -n \
  -b \
  -f -n \
file.xml 

where

  • the //_:* expression matches all element nodes in the default namespace (the _ shortcut is in the xmlstarlet user's guide)
  • the EXSLT function set:distinct eliminates duplicates
  • -o outputs a string literal, -n a newline, -f the input pathname / URL (but - for stdin)
  • -b ends the current container (-m, --var without =, a.o.)
  • adding a -C option before -t lists the generated XSLT code

Output:

xmlstarlet edit --pf \
  -r '//_:svg'  -v 'svg:svg' \
  -r '//_:defs'  -v 'svg:defs' \
file.xml

where

  • -P / --pf preserves original formatting
  • adding an -L / --inplace option after edit supports in-place editing (not in the user's guide but in xmlstarlet.txt)

To execute the output as a shell script:

xmlstarlet-select-command | sh -s > result.xml

In case you want to avoid EXSLT, instead -m '//_:*' --sort 'A:T:-' . and pipe the output through uniq, or simply -m '//_:*' with possible duplicates.

1

Using xmlstarletin a particularly brutal fashion (I await the proper xpath)

for x in $(xmlstarlet sel -t -m "//*" -n -v "name()" file1.xml | sort | uniq); do 
    xmlstarlet ed -r "//svg:$x" -v "svg:$x" file1.xml > tmp.xml;
    mv tmp.xml file1.xml;
done

Since you have declared the namespace svg already, you need to invoke it in the xpath to change the literal value of the node names.

3
  • what is the command xml you use on the second line? Mar 20, 2020 at 19:48
  • the xml command is just an alias for xmlstarlet. Same tool.
    – bu5hman
    Mar 20, 2020 at 22:39
  • OK. In my configuration, the alias xml is not available by default. So I will write xmlstarlet instead. Mar 21, 2020 at 11:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .