1
votes

I am trying to convert some XML with quirky formats to html.

I have something like this

<spec_span span_start="c2" span_end="c9" span_name="ab12" />
...
<table_entry span_name="ab12">Text Value</entry>

I am trying to convert this to

<td colspan="8">Text Value</td>

Roughly what needs to be done is.

  • look up the span_spec with id ab12
  • Strip the 'c' prefix from span_start and span_end
  • subtract the integer value that is left in span_start from span_end
  • add 1 to the final value.

I think that it should be possible to write a function of some sort for it. But string manipulation type casting and maths in XSLT I am not sure about.

2
You can do that. It's possible to select the data you need using an XPath expression. What version of XSLT are you using? It's possible in 1.0 but simpler in 2.0+.helderdarocha
Check out CALS table format conversions to the HTML table model, including my detailed answer to xsl cals tables: span cells, using colspec, namest and nameend.kjhughes
Note that <table_entry> does not match the closing tag </entry>.michael.hor257k

2 Answers

1
votes

Roughly what needs to be done is.

  • look up the span_spec with id ab12
  • Strip the 'c' prefix from span_start and span_end

Lookup in XSLT is best done using a key. And stripping out a known character is easy using the translate() function.

Place this at the top of your stylesheet, outside of any template:

<xsl:key name="spec_span" match="spec_span" use="@span_name" />

Then apply this to the input <table_entry> (?) element:

<xsl:variable name="span" select="key('spec_span', @span_name)" />
<td colspan="{translate($span/@span_end, 'c', '') - translate($span/@span_start, 'c', '') + 1}">Text Value</td>
0
votes

In this possible solution I'm assuming your <spec_span> and <table_entry> are children of a <root> element:

<root>
    <spec_span span_start="c2" span_end="c9" span_name="ab12" />
    ...
    <table_entry span_name="ab12">Text Value</table_entry>
</root>

Selecting spec_span globally you can calculate the colspan with this XPath 1.0 expression:

substring(//spec_span[@span_name='ab12']/@span_end, 2) -  
substring(//spec_span[@span_name='ab12']/@span_start, 2) + 1

You can get the table entry (globally) with:

//table_entry[@span_name='ab12']/text()

In XSLT you can use the root context (or other context) and adapt those expressions. This stylesheet:

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

    <xsl:output method="html" indent="yes"/>

    <xsl:template match="root">
        <xsl:variable name="colspan"
            select="number(substring(spec_span[@span_name='ab12']/@span_end, 2)) - number(substring(spec_span[@span_name='ab12']/@span_start, 2)) + 1"/>
        <xsl:variable name="text"
            select="table_entry[@span_name='ab12']/text()" />
        <td colspan="{$colspan}">
            <xsl:value-of select="$text"/>
        </td>
    </xsl:template>

</xsl:stylesheet>

will produce the following code as a result:

<td colspan="8">Text Value</td>

A better and more general solution would check for matching pairs of spec_span and table_entry (with the same span_name, ignoring incomplete pairs and generating the code for the others. If you have something like this:

<root>
    <spec_span span_start="c2" span_end="c9" span_name="ab12" />
    <table_entry span_name="ab12">Text Value</table_entry>
    <spec_span span_start="c2" span_end="c12" span_name="ab17" />
    <spec_span span_start="c2" span_end="c12" span_name="ab15" />
    <table_entry span_name="ab15">Text Value 2</table_entry>
    <table_entry span_name="ab16">Text Value 3</table_entry>
</root>

you can use this stylesheet (which also works with XSLT 2.0):

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

    <xsl:output method="html" indent="yes"/>

    <xsl:template match="root">
        <xsl:apply-templates select="spec_span[//table_entry/@span_name = @span_name]"/>
    </xsl:template>

    <xsl:template match="spec_span">
        <xsl:variable name="span_name" select="@span_name"/>
        <xsl:variable name="colspan"
            select="number(substring(@span_end, 2)) - number(substring(@span_start, 2)) + 1"/>
        <td colspan="{$colspan}">
            <xsl:value-of select="//table_entry[@span_name=$span_name]"/>
        </td>
    </xsl:template>

</xsl:stylesheet>

which will deal with any spec_span/table_entry pairs (including the example you posted). It will generate two tds for the example above since only two pairs match:

<td colspan="8">Text Value</td>
<td colspan="11">Text Value 2</td>