'Script to update xml value
I am trying to do update on xml file based on condition using AWK Script. Could anyone assist me on this?
students.xml
<students>
<student>
<stuId>1</stuId>
<name>A</name>
<mark>75</mark>
<result></result>
</student>
<student>
<stuId>2</stuId>
<name>B</name>
<mark>35</mark>
<result></result>
</student>
<student>
<stuId>1</stuId>
<name>C</name>
<mark>94</mark>
<result></result>
</student>
</students>
Code I tried so far
I am able to extract tag values using below code
BEGIN { RS="<[^>]+>" }
{ print RT, $0 }
This prints all the tag and values as expected.
I want to update the <result> tag as pass if marks > 40 else fail
Output
<students>
<student>
<stuId>1</stuId>
<name>A</name>
<mark>75</mark>
<result>pass</result>
</student>
<student>
<stuId>2</stuId>
<name>B</name>
<mark>35</mark>
<result>fail</result>
</student>
<student>
<stuId>1</stuId>
<name>C</name>
<mark>94</mark>
<result>pass</result>
</student>
</students>
Could any one assist me on this?
Solution 1:[1]
Don't try to parse XML with awk, instead use a real parser :
the XML file is edited on the fly!
With perl :
#!/usr/bin/env perl
# edit file.xml file in place
use strict; use warnings;
use XML::LibXML;
my $xl = XML::LibXML->new();
my $xml = $xl->load_xml(location => '/tmp/file.xml') ;
for my $node ($xml->findnodes('//student/result')) {
my $mark = $node->findnodes('../mark/text()')->string_value;
$node->removeChildNodes();
if ($mark > 40) {
$node->appendText('pass');
}
else {
$node->appendText('fail');
}
}
$xml->toFile('/tmp/file.xml');
Modified file :
<?xml version="1.0"?>
<students>
<student>
<stuId>1</stuId>
<name>A</name>
<mark>75</mark>
<result>pass</result>
</student>
<student>
<stuId>2</stuId>
<name>B</name>
<mark>35</mark>
<result>fail</result>
</student>
<student>
<stuId>1</stuId>
<name>C</name>
<mark>94</mark>
<result>pass</result>
</student>
</students>
Solution 2:[2]
Another option is to use the ed (edit) command of xmlstarlet...
xmlstarlet ed -L -u "//student[mark >= 40]/result" -v "pass" -u "//student[40 > mark]/result" -v "fail" students.xml
CAUTION: The -L in the command line will edit the file inplace. Be sure to remove it if that is not the behavior you want.
You can also use XSLT 1.0 with xmlstartlet (the tr (transform) command)...
update.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="student[mark >= 40]/result">
<xsl:copy>pass</xsl:copy>
</xsl:template>
<xsl:template match="student[40 > mark]/result">
<xsl:copy>fail</xsl:copy>
</xsl:template>
</xsl:stylesheet>
command line
xmlstarlet tr update.xsl students.xml
Solution 3:[3]
I would also recommend to avoid a XML parser/processor approach here:
If you don't like perl you can use a full XML technology approach by using XSLT:
INPUT:
$ more students.xml
::::::::::::::
students.xml
::::::::::::::
<students>
<student>
<stuId>1</stuId>
<name>A</name>
<mark>75</mark>
<result></result>
</student>
<student>
<stuId>2</stuId>
<name>B</name>
<mark>35</mark>
<result></result>
</student>
<student>
<stuId>1</stuId>
<name>C</name>
<mark>94</mark>
<result></result>
</student>
</students>
XSLT stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform (copy everything) -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- when you reach result take action-->
<xsl:template match="result">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<!-- fetch the value of mark of the parent node -->
<xsl:variable name="mark" select="../mark" />
<xsl:choose>
<!-- if over 40 -->
<xsl:when test="$mark > 40">
<xsl:text>pass</xsl:text>
</xsl:when>
<!-- else -->
<xsl:otherwise>
<xsl:text>fail</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
COMMAND:
$ xsltproc --output students_grade.xml students.xsl students.xml
OUTPUT:
more students_grade.xml
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student>
<stuId>1</stuId>
<name>A</name>
<mark>75</mark>
<result>pass</result>
</student>
<student>
<stuId>2</stuId>
<name>B</name>
<mark>35</mark>
<result>fail</result>
</student>
<student>
<stuId>1</stuId>
<name>C</name>
<mark>94</mark>
<result>pass</result>
</student>
</students>
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 | Glorfindel |
| Solution 2 | |
| Solution 3 | Allan |
