9

For the below xml ,I need to replace <studentStatus> for <studentName>CLASSA</studentName> to <studentStatus>failed</studentStatus>.

<studentFile>
    <student>
        <studentName>CLASSA</studentName>
        <studentStatus>Success</studentStatus>
        <studentActions>
            <studentAction>
                <studentType>Juniour</studentType>
                <studentStatus>Completed</studentStatus>
                <studentMsg/>
            </studentAction>
            <studentAction>
                <studentType>HighSchool</studentType>
                <studentStatus>Completed</studentStatus>
                <studentMsg/>
            </studentAction>
        </studentActions>
    </student>
    <student>
        <studentName>CLASSB</studentName>
        <studentStatus>Success</studentStatus>
        <studentActions>
            <studentAction>
                <studentType>Senior</studentType>
                <studentStatus>Completed</studentStatus>
            </studentAction>
            <studentAction>
                <studentType>Middle</studentType>
                <studentStatus>Completed</studentStatus>
            </studentAction>                         
        </studentActions>
    </student>
</studentFile>

What I got so far,

xmllint -xpath "/studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType="Juniour"]/studentStatus" myxml.xml

now i got the status of the student as Completed , now this value should be changed to Failed . Only for <studentType>Juniour</studentType>. How should I edit the xml inorder to get it as ,

<studentFile>
    <student>
        <studentName>CLASSA</studentName>
        <studentStatus>Success</studentStatus>
        <studentActions>
            <studentAction>
                <studentType>Juniour</studentType>
                <studentStatus>Failed</studentStatus>
                <studentMsg/>
            </studentAction>
            <studentAction>
                <studentType>HighSchool</studentType>
                <studentStatus>Completed</studentStatus>
                <studentMsg/>
            </studentAction>
        </studentActions>
    </student>
    <student>
        <studentName>CLASSB</studentName>
        <studentStatus>Success</studentStatus>
        <studentActions>
            <studentAction>
                <studentType>Senior</studentType>
                <studentStatus>Completed</studentStatus>
            </studentAction>
            <studentAction>
                <studentType>Middle</studentType>
                <studentStatus>Completed</studentStatus>
            </studentAction>                         
        </studentActions>
    </student>
</studentFile>

Can this be done using sed. I know there are tools like xsltproc but not sure if this is installed in all nodes in our cluster .

Any help will be appreciated. Thanks in advance!

Flow
  • 23,572
  • 15
  • 99
  • 156
Neethu Lalitha
  • 3,031
  • 4
  • 35
  • 60

5 Answers5

22

Update value with xmllint in file.xml:

xmllint --shell file.xml << EOF
cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus
set failed
save
EOF

or without here document:

echo -e "cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus\nset failed\nsave" | xmllint --shell file.xml

Update: With bash and XML in a variable:

xml=$(xmllint --shell <(echo "$xml") << EOF
cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus
set failed
save -
EOF
)

or without here document:

xml=$(echo -e "cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus\nset failed\nsave -" | xmllint --shell <(echo "$xml"))
Cyrus
  • 84,225
  • 14
  • 89
  • 153
  • if my xml is in a variable , how can this command be modified? – Neethu Lalitha May 15 '17 at 03:43
  • Hi @Cyrus , I tried adding xml=$(xmllint --shell <(echo "$xml") << EOF cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus set failed save - EOF ) to my script but it says: unexpected EOF while looking for matching `)' , should i enclose them in quotes ? – Neethu Lalitha May 15 '17 at 18:56
  • 1
    Hi Cyrus, For an xml tag that looks like < studentStatus/> , this command is not working . xml=$(echo -e "cd /studentFile/student[studentName='CLASSA']/studentActions/st‌​udentAction[studentT‌​ype='Juniour']/stude‌​ntStatus\nset failed\nsave -" | xmllint --shell <(echo "$xml")) ,it says `/studentFile/student[studentName='CLASSA']/studentActions/st‌​udentAction[studentT‌​ype='Juniour']/stude‌​ntStatus is an empty Node Set`, Can this command be modified in any way? Any help is appreciated! – Neethu Lalitha May 17 '17 at 03:31
  • Unfortunately, I can not reproduce this. – Cyrus May 17 '17 at 05:24
  • 1
    Just found a workaround for this, i stored the new xml node schema in a variable , update it using xmllint and then replaced it via sed. Thanks Cyrus for the help – Neethu Lalitha May 19 '17 at 12:37
3

xlmlint, as the name implies, is for parsing and validating XML, not editing it. If you can install xmlstarlet on your cluster, you can do the following:

xmlstarlet ed --update "/studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus" --value "Failed" *file*
Brian from QuantRocket
  • 5,268
  • 1
  • 21
  • 18
2

In case if xmlstarlet (a command line toolkit to query/edit/check/transform XML documents) is accessible:

xmlstarlet ed -u "//studentAction/studentStatus[preceding-sibling::studentType[1][text() = 'Juniour'] \
           and ancestor::student/studentName[text() = 'CLASSA']]" -v failed students.xml

The above will output the initial XML document with needed replacement


The command details:

ed -u - edit/update mode

//studentAction/studentStatus - xpath expression to select studentStatus element which has:

  • preceding-sibling::studentType[1][text() = 'Juniour'] - preceding sibling element studentType with value Juniour
  • ancestor::student/studentName[text() = 'CLASSA'] - nearest element studentName with value CLASSA
RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105
0

You can try my Xembly command-line tool:

$ xembly --xml file.xml 'XPATH "/studentFile/student[studentName=\'CLASSA\']/studentActions/studentAction[studentType=\'Juniour\']/studentStatus"; SET "failed";'

The full syntax of Xembly is here.

yegor256
  • 102,010
  • 123
  • 446
  • 597
0

Update your xml file through xmllint with --shell.

$ xmllint --shell pom.xml
$ cd //*[local-name()='project']/*[local-name()='parent']/*[local-name()='version']
$ set 1.0.0
$ save
$ quit

You can combile bash with:

xml=$(xmllint --shell pom.xml <<EOF
cd //*[local-name()='project']/*[local-name()='parent']/*[local-name()='version']
set 1.0.0
save -
EOF
)
ozinal
  • 29
  • 3