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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.