0

Given this source XML:

<Objects>
    <Object>
        <Relations>
            <Relation>
                <Placements>
                    <Placement>
                        <Page>6</Page>
                        <Element>body</Element>
                        <FrameOrder>3</FrameOrder>
                        <PageSequence>2</PageSequence>
                        <PageNumber>6</PageNumber>
                    </Placement>
                    <Placement>
                        <Page>1</Page>
                        <Element>body</Element>
                        <FrameOrder>0</FrameOrder>
                        <PageSequence>1</PageSequence>
                        <PageNumber>1</PageNumber>
                    </Placement>
                    <Placement>
                        <Page>6</Page>
                        <Element>body</Element>
                        <FrameOrder>4</FrameOrder>
                        <PageSequence>2</PageSequence>
                        <PageNumber>6</PageNumber>
                    </Placement>
                    <Placement>
                        <Page>1</Page>
                        <Element>head</Element>
                        <FrameOrder>0</FrameOrder>
                        <PageSequence>1</PageSequence>
                        <PageNumber>1</PageNumber>
                    </Placement>
                    <Placement>
                        <Page>1</Page>
                        <Element>body</Element>
                        <FrameOrder>2</FrameOrder>
                        <PageSequence>1</PageSequence>
                        <PageNumber>1</PageNumber>
                    </Placement>
                    <Placement>
                        <Page>1</Page>
                        <Element>body</Element>
                        <FrameOrder>1</FrameOrder>
                        <PageSequence>1</PageSequence>
                        <PageNumber>1</PageNumber>
                    </Placement>
                </Placements>
            </Relation>
        </Relations>
    </Object>
</Objects>

I'm trying to get the distinct Page elements, in order, where the Element elements are body:

<Pages>
    <Page>1</Page>
    <Page>6</Page>
</Pages>

The furthest I seem to get is by using the following XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/> 
<xsl:template match="/">
<xsl:for-each select="Objects/Object/Relations/Relation/Placements/Placement[Element='body']">
<xsl:sort select="FrameOrder"/>
    <Page><xsl:value-of select="Page"/></Page>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

That gives me:

<Pages>
    <Page>1</Page>
    <Page>1</Page>
    <Page>1</Page>
    <Page>6</Page>
    <Page>6</Page>
</Pages>

How do I get the distinct values of Page at this point?

Thanks in advance!

CMarcera
  • 69
  • 7

1 Answers1

1
<xsl:key name="kPage" match="Placement/Page" use="number(.)" />

<xsl:template match="/">
  <Pages>
    <xsl:for-each select="
      //Page[
        generate-id() = generate-id(key('kPage', number(.))[1])
      ]
    ">
      <xsl:sort select="." data-type="number" />
      <xsl:copy-of select="." />
    </xsl:for-each>
  </Pages>
</xsl:template>
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • I had attempted generating a key but kept getting errors. I see now that the ` – CMarcera Oct 10 '12 at 08:07
  • 1
    @CMarcera It's a good habit to put keys at the top of the top of the XSL document. Note that using `number(.)` is not strictlly necessary. If you know that the content of `` will always be an integer, no leading zeros or other anormalities then you can just use `.`. Using `number()` just is more explicit. – Tomalak Oct 10 '12 at 08:16
  • Revisiting this topic, how would I modify the for-each to ignore the numeric value of 0? I didn't realize that would be a possibility in the data I am transforming, but of course I just came across an instance where the first value was `0` which wasn't expected (or desired). – CMarcera Oct 15 '12 at 21:49
  • @CMarcera `//Page[not(number(.) = 0)][...])` of course. Alternatively it's enough to make the key not match any elements with `0`. – Tomalak Oct 15 '12 at 22:16
  • Thanks! Just so I understand your alternative, you mean making the key (`` not matching 0 by changing it to `use="not(number(.) = 0)`"? – CMarcera Oct 16 '12 at 17:45
  • 1
    @CMarcera since you want it to *match* ``s that are not `0`, it would be ``. If you are unsure how XSL keys work, read here http://stackoverflow.com/a/955527/18771 – Tomalak Oct 16 '12 at 17:52