0

I have some XML data (customer data, so arbitrary example:)

 <attributes>
          <attribute>
            <attribute_type xsi:type="xsd:string">
              parent
            </attribute_type>
            <attribute_id xsi:type="xsd:string">
              ABC123
            </attribute_id>
            <someValue xsi:type="xsd:string">
              123456
            </someValue>
          </attribute>

          <attribute>
            <attribute_type xsi:type="xsd:string">
              child
            </attribute_type>
            <attribute_id xsi:type="xsd:string">
              ABC123
            </attribute_id>
            <someValue xsi:type="xsd:string">
              null
            </someValue>
          </attribute>
</attributes>

Each attribute has a 'parent' and 'child', which aren't nested but contain an identifier that provides a commonality between the two. Each Parent should be followed by the Child, so these should always be in order.

When importing the data, I only want to import the 'child', but the parent contains information that I'd like to import with the child (in this case, someValue).

I have tried setting a variable on on iteration so that it can pass a value forwards, i.e:

<xsl:for-each select="attributes/attribute">
<xsl:if test="attribute_type = 'parent' ">
    <xsl:variable name="testId" select="normalize-space(attribute_id)"/> 
    <xsl:variable name="testValue" select="normalize-space(someValue)"/>   
</xsl:if>

<xsl:if test="attribute_type != 'parent' ">
attribute.<xsl:value-of select="position()"/>.id=<xsl:value-of select="normalize-space(attribute_id)"/>

    <xsl:if test="attribute_id = $testId">
        attribute.<xsl:value-of select="position()"/>.Value=<xsl:value-of select="$testValue"/>
    </xsl:if>
</xsl:for-each>

There is obviously a lot of data & values missing here, but I've slimmed this down to keep it readable. Essentially, I'm looking to only output the 'child' values, but I want to pass the parent values to the child's output via variables.

The expected output would be:

  attribute.2.id    = ABC123
  attribute.2.Value = 123456

(I'm expecting a first position() of '2' due to the iteration but non-output of the first parent as position() = 1)

The above doesn't work, and I think it may be to do with variable scoping.

The position() is making it difficult to simply just 'output' the data, unless I 'bodge' it with a <xsl:value-of select="position()+1"/> kind of thing to try and push it forwards, but I'm not sure that's going to be robust or reliable at all.

Is this going to be possible (working on the fact that I can't amend the original XML data)?

2
  • Can you edit your question to show the output you expect in this case? Thank you. Commented Nov 2, 2017 at 12:44
  • Hi @TimC - I've added some detail to show what output is expected. This is arbitrary for a clear example, but in a real-life situation it would output a number of attributes from the child data (e.g. a host of personal data), yet would also retain one field that is only present in the parent, but output as part of the child data - if that makes sense? Commented Nov 2, 2017 at 13:34

1 Answer 1

1

If you have a cross-reference then use a key to follow that and access the value:

<xsl:key name="parent" match="attribute[normalize-space(attribute_type) = 'parent']" use="normalize-space(attribute_id)"/>

<xsl:template match="/">
  <xsl:for-each select="attributes/attribute[normalize-space(attribute_type) != 'parent']">
      attribute.<xsl:number/>.id=<xsl:value-of select="normalize-space(attribute_id)"/>
      <xsl:if test="key('parent', normalize-space(attribute_id))">
          attribute.<xsl:number/>.Value=<xsl:value-of select="normalize-space(key('parent', normalize-space(attribute_id))/someValue)"/>
      </xsl:if>
  </xsl:for-each>
</xsl:template>

Another way would be to simply navigate to preceding-sibling::attribute[1][normalize-space(attribute_id) = normalize-space(current()/attribute_id)] from within the for-each.

If you really want a construct to "iterate" sequentially and store variables on each step then move to XSLT 3 and xsl:iterate (https://www.w3.org/TR/xslt/#iterate).

Sign up to request clarification or add additional context in comments.

1 Comment

Excellent - that's exactly what I was looking to do. Unfortunately I'm not able to use XSLT3, but I managed to get it working using the <xsl:key> method. Thanks for you help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.