tags:

views:

77

answers:

2

My XML file looks like this:

<Configuration>
    <Parameters>
        <Component Name="Aas">
            <Group Name="PrivilegesMapping">
                <Parameter Name="PrivilegesLoaderInterval">
                    <Description>
                    The interval (in minute) 
                    </Description>
                    <Type>Integer</Type>
                    <Restriction>
                        <RequiresRestart>true</RequiresRestart>
                        <MinVal/>
                        <MaxVal/>
                        <MaxLength/>
                        <Mandatory>true</Mandatory>
                        <Lov/>
                        <Level>5</Level>
                    </Restriction>
                    <Value>
                        <Item Value="5"/>
                    </Value>
                </Parameter>
            </Group>
            <Group Name="DomainsMapping">
                <Parameter Name="DomainLoaderInterval">
                    <Description>
                    The interval (in minute) 
                    </Description>
                    <Type>Integer</Type>
                    <Restriction>
                        <RequiresRestart>true</RequiresRestart>
                        <MinVal/>
                        <MaxVal/>
                        <MaxLength/>
                        <Mandatory>true</Mandatory>
                        <Lov/>
                        <Level>5</Level>
                    </Restriction>
                    <Value>
                        <Item Value="5"/>
                    </Value>
                </Parameter>
                <Parameter Name="MapSource">
                    <Description>
                    Set the source of the domains list
                    </Description>
                    <Type>Enum</Type>
                    <Restriction>
                        <RequiresRestart>true</RequiresRestart>
                        <MinVal/>
                        <MaxVal/>
                        <MaxLength/>
                        <Mandatory>true</Mandatory>
                        <Lov>
                            <Val>FILE</Val>
                            <Val>DATABASE</Val>
                            <Val>NONE</Val>
                        </Lov>
                        <Level>5</Level>
                    </Restriction>
                    <Value>
                        <Item Value="FILE"/>
                    </Value>
                </Parameter>
            </Group>
            <Group Name="SystemsMapping">
                <Parameter Name="MapSource">
                    <Description>
                    </Description>
                    <Type>Enum</Type>
                    <Restriction>
                        <RequiresRestart>true</RequiresRestart>
                        <MinVal/>
                        <MaxVal/>
                        <MaxLength/>
                        <Mandatory>true</Mandatory>
                        <Lov>
                            <Val>API</Val>
                            <Val>FILE</Val>
                            <val>NONE</Val>
                        </Lov>
                        <Level>5</Level>
                    </Restriction>
                    <Value>
                        <Item Value="NONE"/>
                    </Value>
                </Parameter>
                <Parameter Name="SystemsLoaderInterval">
                    <Description>
                    The interval (in minute) 
                    </Description>
                    <Type>Integer</Type>
                    <Restriction>
                        <RequiresRestart>true</RequiresRestart>
                        <MinVal/>
                        <MaxVal/>
                        <MaxLength/>
                        <Mandatory>true</Mandatory>
                        <Lov/>
                        <Level>5</Level>
                    </Restriction>
                    <Value>
                        <Item Value="5"/>
                    </Value>
                </Parameter>
            </Group>
        </Component>
    </Parameters>
</Configuration>

I'd like to change value from <Item Value="NONE"/> to <Item Value="API"/> under <Parameter Name="MapSource">.

A: 

Remembering that processing XML with regular expressions is usually a really bad idea, with Perl you could

#! /usr/bin/perl

use warnings;
use strict;

system("xsltproc", "fix.xsl", "input.xml") == 0
  or warn "$0: xsltproc failed\n";

and with fix.xsl of

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/Configuration/Parameters/Component/Group/Parameter[@Name='MapSource']/Value/Item[@Value='NONE']">
    <xsl:element name="Item">
      <xsl:attribute name="Value">API</xsl:attribute>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

to get the desired result:

$ diff -ub input.xml <(./prog.pl)
--- input.xml
+++ /dev/fd/63
@@ -1,3 +1,4 @@
+<?xml version="1.0"?>
 <Configuration>
     <Parameters>
         <Component Name="Aas">
@@ -82,7 +83,7 @@
                         <Level>5</Level>
                     </Restriction>
                     <Value>
-                        <Item Value="NONE"/>
+                        <Item Value="API"/>
                     </Value>
                 </Parameter>
                 <Parameter Name="SystemsLoaderInterval">

In case you aren't familiar, <(./prog.pl) uses bash process substitution, so the diff command is comparing input.xml with the output of the short Perl program.

To replace all Item elements that are descendants of MapSource parameters, use the stylesheet below. Note how it's more flexible about the structure of the source document.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="//Parameter[@Name='MapSource']//Item">
    <xsl:element name="Item">
      <xsl:attribute name="Value">API</xsl:attribute>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Now the change from input to output is

$ diff -ub input.xml <(./prog.pl)
--- input.xml
+++ /dev/fd/63
@@ -1,3 +1,4 @@
+<?xml version="1.0"?>
 <Configuration>
     <Parameters>
         <Component Name="Aas">
@@ -59,7 +60,7 @@
                         <Level>5</Level>
                     </Restriction>
                     <Value>
-                        <Item Value="FILE"/>
+                        <Item Value="API"/>
                     </Value>
                 </Parameter>
             </Group>
@@ -82,7 +83,7 @@
                         <Level>5</Level>
                     </Restriction>
                     <Value>
-                        <Item Value="NONE"/>
+                        <Item Value="API"/>
                     </Value>
                 </Parameter>
                 <Parameter Name="SystemsLoaderInterval">
Greg Bacon
I loved this solution. Thank you. Q: How I can change the fix.xsl to avoid use 'NONE' in path [@Value='NONE'] . I do not want care what string currently occupied <Item Value >
Toren
@Toren Thanks! See updated answer.
Greg Bacon
A: 

The quickest (easiest) way I found was XML::XPath (even smaller with File::Slurp -- see edit history for pre-slurp code):

use strict;
use warnings;
use File::Slurp ();
use XML::XPath;
use XML::XPath::XMLParser;

my $path = '/path/to/file/config.xml';
my $xp   = XML::XPath->new( filename => $path );
$xp->setNodeText( q{//Parameter[@Name='MapSource']/Value/Item/@Value} 
                , 'API'
                );
File::Slurp::write_file( $path, $xp->findnodes_as_string( '/' ));
Axeman
Thanks . Nice solution
Toren