0

I'm a relative novice with XSLT, not having touched it since the late 90s, but I've recently started on a personal project that will involve it.

I have the following XML, from which I'm attempting to build an HTML character sheet:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="./Statblock.xslt"?>
<Character>
 <Name value="Seiyatomo"/>
 <Family value="Soshi"/>
 <Clan value="Scorpion"/>
 <School value="Soshi Illusionist School"/>
 <Titles/>
 <Ninjo value=""/>
 <Giri value=""/>
 <Abilities>
  <Ability description="" name="The Kami's Whisper" />
 </Abilities>
 <Skills>
  ...
 </Skills>
 <Rings>
  ...
 </Rings>
 <Social glory="50" honor="35" status="35"/>
 <Wealth bu="0" koku="6" zeni="0"/>
 <Derived composure="8" endurance="4" focus="4" vigilance="3"/>
 <RankStatus titlestatus="Title: , Title XP: 0" curricstatus="Rank: 1, XP in Rank: 0"/>
 <Curriculum>
    ...
 </Curriculum>
 <Title/>
 <Techniques>
  <Technique name="Bō of Water" />
  <Technique name="Cloak of Night" />
  <Technique name="Token of Memory" />
  <Technique name="Commune with the Spirits" />
  <Technique name="All in Jest" />
  <Technique name="Dangerous Allure" />
  <Technique name="Shadowlands Taint (Water)" />
  <Technique name="Curiosity" />
  <Technique name="Dark Secret" />
  <Technique name="Fallen Ancestor" />
 </Techniques>
 <PersonalTraits/>
 <Equipment>
    ...
 </Equipment>
 <Heritage value="Glorious Sacrifice"/>
 <Notes value="..."/>
 <Advances/>
 <TotalXP value="0"/>
 <XPSpent value="0"/>
 <Portrait base64image=""/>
</Character>

I am trying to group the combined list of Ability & Technique nodes into sets of 9, such that I'll get output akin to this:

<page>
  <ability>The Kami's Whisper</ability>
  <technique>Bō of Water</technique>
  <technique>Cloak of Night</technique>
  <technique>Token of Memory</technique>
  <technique>Commune with the Spirits</technique>
  <technique>All in Jest</technique>
  <technique>Dangerous Allure</technique>
  <technique>Shadowlands Taint (Water)</technique>
  <technique>Curiosity</technique>
</page>
<page>
  <technique>Dark Secret</technique>
  <technique>Fallen Ancestor</technique>
</page>

The number of <Ability> and <Technique> nodes in the XML are arbitrary, but I would prefer that all <ability> nodes come first in the output, followed by the <technique> nodes.

Bonus points, if that last <page> can be full of empty <technique> entries to fill the page out to the full set of 9, but that's a secondary concern.

I tried searching for similar questions, but either I didn't see any that were quite like this, or I didn't understand the question/answers sufficiently to recognize them as duplicates.

Note: It may be possible to get the author of the application which generates the XML to make some changes to the XML if necessary, but I can't directly control the XML itself.

Theo Brinkman
  • 291
  • 2
  • 10
  • Please state which version of XSLT your processor supports. – michael.hor257k Aug 14 '19 at 04:44
  • There's versions of XSLT? – Theo Brinkman Aug 14 '19 at 13:04
  • Yes. As I said in the question, the last time I did anything significant with XSLT was in the late 90s. So far, none of the XSLT tutorials I've found have mentioned different versions. – Theo Brinkman Aug 14 '19 at 16:28
  • I see. In any case, if you want to move forward to the "bonus points", you need to answer my question. If you don't know, see here how to find find out: https://stackoverflow.com/questions/25244370/how-can-i-check-which-xslt-processor-is-being-used-in-solr/25245033#25245033 – michael.hor257k Aug 14 '19 at 16:50
  • I've got a question out to the author of the application regarding what version of XSLT his libraries support. I'll update with another comment when I get a response. – Theo Brinkman Aug 17 '19 at 23:17
  • @michael.hor257k, I just heard back from the author. His toolset supports XSLT 2.0. – Theo Brinkman Aug 21 '19 at 17:49

1 Answers1

1

At least the first part seems simple enough:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/Character">
    <xsl:copy>
        <xsl:copy-of select="Name" />  
        <xsl:call-template name="paginate">
            <xsl:with-param name="nodes" select="Abilities/Ability | Techniques/Technique"/>
        </xsl:call-template>    
    </xsl:copy>
</xsl:template>

<xsl:template match="Ability">
    <ability>
        <xsl:value-of select="@name"/>
    </ability>
</xsl:template>

<xsl:template match="Technique">
    <technique>
        <xsl:value-of select="@name"/>
    </technique>
</xsl:template>

<xsl:template name="paginate">
    <xsl:param name="nodes"/>
    <xsl:param name="pagesize" select="9"/>
    <page>
        <xsl:apply-templates select="$nodes[position() &lt;= $pagesize]"/>
    </page>
    <xsl:if test="count($nodes) > $pagesize">
        <xsl:call-template name="paginate">
            <xsl:with-param name="nodes" select="$nodes[position() > $pagesize]"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

Even simpler in XSLT 2.0:

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/Character">
    <xsl:copy>
        <xsl:copy-of select="Name" /> 
        <xsl:for-each-group select="Abilities/Ability | Techniques/Technique" group-by="(position()-1) idiv 9">
            <page>
                <xsl:apply-templates select="current-group()"/>
            </page>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<xsl:template match="Ability | Technique">
    <xsl:element name="{lower-case(name())}">
        <xsl:value-of select="@name"/>
    </xsl:element>      
</xsl:template>

</xsl:stylesheet>

For the second part, just add (in XSLT 2.0):

<xsl:for-each select="1 to 9 - count(current-group())">
    <technique/>
</xsl:for-each>

Demo: https://xsltfiddle.liberty-development.net/gWvjQgq

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51