Search code examples
xmlxsltxmlstarlet

Replace value taken from another property


I am trying to duplicate a value within another one already existing that is different everytime using xmlstarlet and I don't find a way to do it. Here's a example:

Current XML:

<MsgList>
    <Msg N="/Workflow/StateObject" I="0" T="03 May 2012 09:32:32.795000" S="Inquiry" D="Inquiry">
        <M N="eventid">999999</M>
        <M N="hist-time">19 Oct 2017 10:50:35 Etc/UTC</M>
        <M N="hist-user">System</M>
        <M N="key">1</M>
        <M N="key-so">357251</M>
        <G N="_StateObject">
            <F N="CurrentState" T="N">44</F>
            <F N="Version" T="S">16.16</F>
            <F N="Status" T="S">OK</F>
        </G>
    </Msg>
    <Msg N="/Workflow/StateObject" I="0" T="05 Feb 2013 15:26:32.971000" S="Inquiry" D="Inquiry">
        <M N="eventid">999999</M>
        <M N="hist-time">19 Oct 2017 10:50:36 Etc/UTC</M>
        <M N="hist-user">System</M>
        <M N="key">2</M>
        <M N="key-so">618751</M>
        <G N="_StateObject">
            <F N="CurrentState" T="N">44</F>
            <F N="Version" T="S">16.16</F>
            <F N="Status" T="S">OK</F>
        </G>
    </Msg>
</MsgList>

Desired XML:

<MsgList>
    <Msg N="/Workflow/StateObject" I="0" T="03 May 2012 09:32:32.795000" S="Inquiry" D="Inquiry">
        <M N="eventid">999999</M>
        <M N="hist-time">19 Oct 2017 10:50:35 Etc/UTC</M>
        <M N="hist-user">System</M>
        <M N="key">357251</M>
        <M N="key-so">357251</M>
        <G N="_StateObject">
            <F N="CurrentState" T="N">44</F>
            <F N="Version" T="S">16.16</F>
            <F N="Status" T="S">OK</F>
        </G>
    </Msg>
    <Msg N="/Workflow/StateObject" I="0" T="05 Feb 2013 15:26:32.971000" S="Inquiry" D="Inquiry">
        <M N="eventid">999999</M>
        <M N="hist-time">19 Oct 2017 10:50:36 Etc/UTC</M>
        <M N="hist-user">System</M>
        <M N="key">618751</M>
        <M N="key-so">618751</M>
        <G N="_StateObject">
            <F N="CurrentState" T="N">44</F>
            <F N="Version" T="S">16.16</F>
            <F N="Status" T="S">OK</F>
        </G>
    </Msg>
</MsgList>

"key" needs to have the same value of "key-so"; my xslt file now is this:

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

    <xsl:template match="node()|@*">
        <xsl:attribute name="xgenkey">
            <xsl:text>for pros</xsl:text>
        </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>

It's possible to do it? How can I do that using this tools?


Solution

  • Your second template should really the M element which has a N attribute of "key"

    <xsl:template match="M[@N='key']">
    

    Then, inside the template, to replace the value, you can get the new value like this (where .. represents the parent node)

    <xsl:value-of select="../M[@N='key-so']" />
    

    Try this XSLT

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="M[@N='key']">
            <xsl:copy>
                <xsl:apply-templates select="@*" />
                <xsl:value-of select="../M[@N='key-so']" />
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    Actually, you could simplify it slightly, by matching the child text node directly. This would also work (assuming M[@N='key'] always had a child text node present).

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="M[@N='key']/text()">
            <xsl:value-of select="../../M[@N='key-so']" />
        </xsl:template>
    </xsl:stylesheet>