'How to insert existing xml line using xmlstarlet

I've tried searching and reading the xmlstarlet help and I apologize if I missed the answer. I'm trying to take an existing line of XML and add it to an xml file using xmlstarlet. ie:

I have a line like this: <somedata name="aname" val="aval"/>

and an xml file similar to

<xml>
  <subxml/>
</xml>

I would like the output to be

<xml>
  <subxml>
    <somedata name="aname" val="aval"/>
  </subxml>
</xml>

I haven't been able to find a way to either just echo the line i have and insert it or some other method without having to parse the line and then insert used all the xmlstarlet edit options (-n -v...).

Thanks in advance for the help!



Solution 1:[1]

a workaround, to insert raw XML code with xmlstarlet

  1. insert a temporary xml node with xmlstarlet
  2. replace that node with sed

the temp node must be unique in the file

file="input.xml" # will be modified in-place
xpath="/xml/subxml"
raw_xml='<foo type="asdf">pipe||||amp&&&&back\0\1\2\3</foo>'
esc_xml=$(echo "$raw_xml" | sed -e 's/[\/&]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')
temp=node$(date | sha256sum | cut -d' ' -f1)
xmlstarlet ed -L -s "$xpath" -t elem -n $temp "$file"
sed -i "s|<$temp/>|$esc_xml|" "$file"

this will use a temp node like <node6044c3e85d715b423cafd58c17bbda2748d7b51d9481b11c792a5313ddced18b/> which should be safe from collisions

related: https://superuser.com/questions/422459/substitution-in-text-file-without-regular-expressions/

Solution 2:[2]

As a quick answer I wound up appending the lines I wanted to the end of the existing file, and then just using a move command in xmlstarlet to put the data in the right spot and format it correctly. Also, I removed the last closing tag and re-added just to be sure it properly formatted xml. It's creative but not the best way to do it, but it works. Basically it looked something like this:

sed -i -e 's/<\/xml>//' file.xml
echo "<somedata name="aname" val="aval"/>" >> file.xml
echo "</xml>
xmlstarlet ed -L -O -m //somedata //xml//subxml file.xml

If anyone has a better way to accomplish this I'd still be very interested.

Solution 3:[3]

It's really bad practice to use sed to edit xml-files. xmlstarlet can do this just fine:

$ xmlstarlet ed -O \
    -s '//subxml' -t elem -n somedata \
    -i '//subxml/somedata' -t attr -n name -v aname \
    -i '//subxml/somedata' -t attr -n val -v aval \
  input.xml
<xml>
  <subxml>
    <somedata name="aname" val="aval"/>
  </subxml>
</xml>

is another option:

$ xidel -s input.xml -e '
  x:replace-nodes(
    //subxml,
    <subxml><somedata name="aname" val="aval"/></subxml>
  )
' --output-node-format=xml --output-node-indent
<xml>
  <subxml>
    <somedata name="aname" val="aval"/>
  </subxml>
</xml>

$ xidel -s input.xml -e '
  x:replace-nodes(
    //subxml,
    element subxml {
      element somedata {
        attribute name {"aname"},
        attribute val {"aval"}
      }
    }
  )
' --output-node-format=xml --output-node-indent
<xml>
  <subxml>
    <somedata name="aname" val="aval"/>
  </subxml>
</xml>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Dan Fumosa
Solution 3