0

I have the following xml file:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:aut="http://">
   <soapenv:Header>
      <aut:Session>
         <aut:IPAddress>127.0.0.1</aut:IPAddress>
         <aut:SessionToken>true</aut:SessionToken>
         <aut:ApplicationToken>861</aut:ApplicationToken>
      </aut:Session>
   </soapenv:Header>
   <soapenv:Body></soapenv:Body>
</soapenv:Envelope>

What is the best way to replace <aut:SessionToken>true</aut:SessionToken> by <aut:SessionToken>false</aut:SessionToken> ?

Here is what I'm trying:

xmllint --shell file.xml << EOF
cd //*[local-name() = "Header"]/*[local-name() = "Session"]/text()/*[local-name() = "SessionToken"]/text()
set failed
save
EOF

I'm having problems because of namespace when I try to replace true for false.

Br, JD

4 Answers 4

2

When dealing with a SOAP envelope I wouldn't use *[local-name() = "…"] which ignores the namespace. Instead, use an explicit namespace binding.

To toggle the boolean, for example

xmlstarlet edit -N aut="http://" \
    --var T '//aut:Session/aut:SessionToken' \
    -u '$T' -x 'not($T)' file.xml 

Add -L / --inplace before -N to edit the file in-place.

To read its value:

xmlstarlet select -N aut="http://" \
    -t -v '//aut:Session/aut:SessionToken' -n file.xml
Sign up to request clarification or add additional context in comments.

Comments

1

You're almost there. You just have an extra text() element in there. In xmlstartlet, try

    xml ed -u '//*[local-name() = "Header"]/*[local-name() = "Session"] \
//*[local-name() = "SessionToken"]//text()' -v "false" yourfile.xml

Comments

1

Your approach with xmllint is not far from correct. Just needs to add namespace handling and use namespace prefixes in XPath expression.

setrootns
cd //soapenv:Header/aut:Session/aut:SessionToken
set failed
save

As a one-liner:

echo -e "setrootns\n cd //soapenv:Header/aut:Session/aut:SessionToken\n set failed\n save\n bye" | xmllint --shell test.xml

2 Comments

This approach is much more readable instead //*[local-name()…. @LMC However, would you mind to explain the purpose of setrootns? Because it seems to be mandatory as it won't work without it?
setrootns makes the parser take into account namespaces declared in root element so prefixes can be used in xpath expressions. local-name() does not need namespaces at expenses of a more complex/hard to read expression. To take a look at xmllint shell commands run xmllint --shell any.xml and then issue a help command.
1

Howto: Debugging the XML structure and replacing values for a node

As Google might lead someone else here who has to alter his own XML file with a different structure and nodes, it could be arduous finding the proper syntax in order to change values.

Sidenode: eventough xmlstarlet might be the better tool for replacing values in XML files, most Unices have xmllint pre-installed.

Thus, the following practice shows how to navigate with xmllint in interactive mode (i. e. debug) through any xml file in order to find the Node (i.e syntax) which should be replaced.

xmllint --shell file.xml        # starts xmllint in interactive mode
setrootns                       # see LMCs explanation
cat                             # shows the complete XML structure

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:aut="http://">
   <soapenv:Header>
      <aut:Session>
         <aut:IPAddress>127.0.0.1</aut:IPAddress>
         <aut:SessionToken>true</aut:SessionToken>
         <aut:ApplicationToken>861</aut:ApplicationToken>
      </aut:Session>
   </soapenv:Header>
   <soapenv:Body></soapenv:Body>
</soapenv:Envelope>

Now you can walk step by step to the desired node through the XML tree :

cd //soapenv:Envelope                               # change to the first level
soapenv:Envelope >                                  # the prompt changes on success
cd //soapenv:Envelope/soapenv:Header/aut:Session
aut:Session > cat                                   # appropriate prompt change

<aut:Session>
     <aut:IPAddress>127.0.0.1</aut:IPAddress>
     <aut:SessionToken>true</aut:SessionToken>
     <aut:ApplicationToken>861</aut:ApplicationToken>
</aut:Session>

Show directly the structure and values for a certain node/path (without prior cd):

cat //soapenv:Envelope/soapenv:Header/aut:Session

<aut:Session>
     <aut:IPAddress>127.0.0.1</aut:IPAddress>
     <aut:SessionToken>true</aut:SessionToken>
     <aut:ApplicationToken>861</aut:ApplicationToken>
 </aut:Session>

Bear in mind to not have a trailing slash at the end of the path as it won't find the node:

cat //soapenv:Envelope/soapenv:Header/aut:Session/    # trailing slash throws an error

XPath error : Invalid expression
//soapenv:Envelope/soapenv:Header/aut:Session/
                                             ^
//soapenv:Envelope/soapenv:Header/aut:Session: no such node

Assuming we would like to change the IP address, its a good idea to check first the proper path:

cat //soapenv:Envelope/soapenv:Header/aut:Session/aut:IPAddress

<aut:IPAddress>127.0.0.1</aut:IPAddress>

Or just get the value of the node:

cat //soapenv:Envelope/soapenv:Header/aut:Session/aut:IPAddress/text()

127.0.0.1

Save first the original file as backup.xml, then apply changes to file.xml, save it and leave xmllint:

 cd //soapenv:Envelope/soapenv:Header/aut:Session/aut:IPAddress
 aut:IPAddress > cat text()         # alternative way to check the value
 127.0.0.1
 aut:IPAddress > save backup.xml    # create backup file
 aut:IPAddress > set 1.1.1.1        # change the value
 aut:IPAddress > cat text()         # crosscheck the changed value
 1.1.1.1
 aut:IPAddress > save               # save changes to file.xml
 aut:IPAddress > quit

In interactive mode help will show further details about commands. The section #Shell at xmllint might also be a good resource.

As soon as the proper path for the node (which should be changed) has been identified, you can refer to the more efficient one-liner as mentioned by @LMC to change XML files on the fly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.