2

Given a node, eg.

<SI elem1="TI" elem2="FN" elem3="4099450222" elem4="TM" elem5="4094110000" elem6="MT" elem7="SP" elem8="MC" elem9="DS" elem10="DA" elem11="16"/>

I need my output to be "DA" if any attribute is "DA", or the value of the next attribute if any attribute is "BA" (i.e. if elem7="BA elem8="03" I want "03" output)

There is no danger of multiple matches, so if an attribute is "BA", there will be no "DA" attribute, but the values could occur in any element

I've looked into the attribute:: tag, but I'm not sure if this will fulfil my needs.

any help greatly appreciated

Lukasz
  • 7,572
  • 4
  • 41
  • 50
Jaloopa
  • 722
  • 1
  • 6
  • 21
  • 1
    Hmm, relying on the order of attributes in your input XML is normally shakey ground. See http://stackoverflow.com/questions/2287861/xslt-display-attributes-in-specific-order. In any case, I don't believe XSL's axes that check pre/pro-ceding nodes can be applied to attributes. – Mitya Jun 14 '12 at 09:29
  • The attribute names will always be elem1, elem2 etc. Is there a way to get the attribute name then replace the numeric characters at the end? – Jaloopa Jun 14 '12 at 10:16
  • To get the name of a node or attribute use `name()` – Mitya Jun 14 '12 at 11:17
  • @Jaloopa it can be done using substring-after XPATH method. see my answer. – Lukasz Jun 14 '12 at 11:44

2 Answers2

3

I made an assumption that your attributes has names in form of elemN where N = 1,2,3..., and they are ordered accordingly.

The following XSLT:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="text" />
    <xsl:template match="/SI">
        <xsl:choose>
            <xsl:when test="some $i in @* satisfies $i='DA'">
                <xsl:text>DA</xsl:text>
            </xsl:when>
            <xsl:otherwise>
                <xsl:variable name="attr" select="concat('elem', xs:decimal(substring-after(@*[.='BA']/name(), 'elem')) + 1)" />
                <xsl:value-of select="@*[name() = $attr]" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

applied to the following input XML:

<?xml version="1.0" encoding="UTF-8"?>
<SI elem1="TI" elem2="FN" elem3="4099450222" elem4="TM" elem5="4094110000" elem6="MT" elem7="SP" elem8="MC" elem9="DS" elem10="DA" elem11="16" />

gives DA as the output.

And applied to the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<SI elem1="TI" elem2="FN" elem3="4099450222" elem4="TM" elem5="4094110000" elem6="MT" elem7="BA" elem8="03" elem9="DS" elem10="DAs" elem11="16" />

gives 03 as the output.

EDIT

Here's the XSLT 1.0 version (tested under Altova XMLSpy):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    <xsl:template match="/">
        <xsl:apply-templates select="SI/@*" />
    </xsl:template>

    <xsl:template match="@*">
        <xsl:choose>
        <xsl:when test=". = 'DA'">
            <xsl:text>DA</xsl:text>     
        </xsl:when>
        <xsl:when test=".='BA'">
            <xsl:variable name="attr" select="concat('elem', substring-after(name(), 'elem') + 1)" />
            <xsl:value-of select="/SI/@*[name() = $attr]" />
        </xsl:when>
        <xsl:otherwise/>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>
Lukasz
  • 7,572
  • 4
  • 41
  • 50
  • Accepted as it solves the problem, and I found it slightly more readable than the answer by @dimitre below, but I managed to get into the C# code that generates the XML and set a more easily obtainable attribute. – Jaloopa Jun 14 '12 at 14:53
  • does this rely on version 2.0? When trying the variable in a 1.0 stylesheet I get the following error: Expected token ')', found '('. ...(substring-after(@*[.='TM']/name -->(<-- ), 'elem')) + 1) – Jaloopa Jun 15 '12 at 14:53
  • @Jaloopa: as you can see in the stylesheet header it is for XSLT 2.0. You haven't mentioned that you wanted XSLT 1.0 solution... – Lukasz Jun 15 '12 at 14:59
2

I need my output to be "DA" if any attribute is "DA", or the value of the next attribute if any attribute is "BA" (i.e. if elem7="BA elem8="03" I want "03" output)

There is no danger of multiple matches, so if an attribute is "BA", there will be no "DA" attribute, but the values could occur in any element

This single XPath expression produces the wanted value:

  string(/*/@*[. = 'DA']
        |
         /*/@*[name()
              =
               concat('elem', substring-after(name(/*/@*[.='BA']), 'elem') +1)]
         )

And here is the complete transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="/">
     <xsl:copy-of select=
     "string(/*/@*[. = 'DA']
            |
             /*/@*[name()
                  =
                   concat('elem', substring-after(name(/*/@*[.='BA']), 'elem') +1)]
             )"/>
 </xsl:template>
</xsl:stylesheet>

As can be seen this transformation simply evaluates the XPath expression and copies the result of the evaluation to the output.

When the transformation is applied on this XML document (your 2nd case):

<SI elem1="TI"
    elem2="FN"
    elem3="4099450222"
    elem4="TM"
    elem5="4094110000"
    elem6="MT"
    elem7="BA"
    elem8="03"
    elem9="DS"
    elem10="DD"
    elem11="16"/>

the result is:

03

When the same transformation is applied on the originally provided XML document (your 1st case):

<SI elem1="TI"
    elem2="FN"
    elem3="4099450222"
    elem4="TM"
    elem5="4094110000"
    elem6="MT"
    elem7="SP"
    elem8="MC"
    elem9="DS"
    elem10="DA"
    elem11="16"/>

again the wanted, correct result is produced:

DA

Explanation:

Proper use of the XPath union operator |, and the functions string(), substring-after(), name() and `concat().

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431