Flattening part of a structure

A

aamcfarland

Hi,

I have an XML document that looks like this:

<srcdoc>
<para>Paragraph</para>
<para>Paragraph with cross-ref <xref/> and ordered list.
<olist>
<item>First item</item>
</olist>
Followed by more text.
</para>
</srcdoc>

and I want to transform it into this:

<doc>
<p>Paragraph</p>
<pParagraph with cross-ref and ordered list</p>
<ol>
<li>First item</li>
</ol>
<p>Followed by more text</p>
</doc>

This is mostly a trivial transform. I can almost do it with this XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/
Transform">
<xsl:eek:utput omit-xml-declaration="yes"/>

<xsl:template match="/">
<doc>
<xsl:apply-templates/>
</doc>
</xsl:template>

<xsl:template match="para">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>

<xsl:template match="olist">
<ol>
<xsl:apply-templates/>
</ol>
</xsl:template>

<xsl:template match="item">
<li>
<xsl:apply-templates />
</li>
</xsl:template>

</xsl:stylesheet>

But this gives me the following output:

<doc>
<p>Paragraph</p>
<p>Paragraph with cross-ref and ordered list.
<ol>
<li>First item</li>
</ol>
Followed by more text.
</p>
</doc>

That is the <ol/> is a child of the <p/> rather than a sibling.

I know the standard DocBook to HTML does this, but I just can't work
out how it does it.
 
J

Joseph Kesselman

If you really want to flatten the para's content to follow the p rather
than be within it, you could change your stylesheet to say that:

<xsl:template match="para">
<p></p>
<xsl:apply-templates/>
</xsl:template>

I submit, however, that this is almost certainly a Bad Idea. You're
trying to deliberately emulate a *misuse* of HTML. Even in basic HTML, a
paragraph's content *SHOULD* be between the <p> and </p> tags; the only
difference is that HTML magically inserts </p> wherever it thinks
they're needed. Thinking about it as "<p> before" works, but really is
wrong... and you certainly don't want the </p> before in any case.
 
A

aamcfarland

If you really want to flatten the para's content to follow the p rather
than be within it,

That's not quite what I want. I want all the content of the <para/> to
be in a <p/>, except for <olist/> content, which should be in a
sibling to the <p/>, and any following <para/> content goes into a
following <p/>

e.g,

<para>Para content before list <olist/> Para content after list</para>

to

<p>Para content before list</p>
<ol/>
<p>Para content after list</p>
 
J

Joseph Kesselman

That's not quite what I want. I want all the content of the <para/> to
be in a <p/>, except for <olist/> content, which should be in a
sibling to the <p/>, and any following <para/> content goes into a
following <p/>

So you're trying to express:
<p>
<apply-templates for everything before the first olist/>
</p>
<apply-templates for the first olist/>
<apply-templates for everything following the first olist>

.... right?

Should be doable. Let me ponder.
 
A

aamcfarland

So you're trying to express:
<p>
<apply-templates for everything before the first olist/>
</p>
<apply-templates for the first olist/>
<apply-templates for everything following the first olist>

... right?

Nearly. There could be any number of <olist/> elements.

<p>
<apply-templates for everything before the first olist/>
</p>
<apply-templates for the first olist/>
<apply-templates for everything until the next olist>
<apply-templates for the second olist/>
Should be doable. Let me ponder.

Thanks :)
 
P

Pavel Lepin

A little something to get the OP started:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="unwrap"
match="para/node()[not(self::eek:list)]"
use=
"
concat
(
generate-id(..),'|',
generate-id(preceding-sibling::eek:list[1])
)
"/>
<xsl:key name="unwrap" match="para/olist"
use="concat(generate-id(..),'||')"/>
<xsl:eek:utput indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="para">
<xsl:copy>
<xsl:apply-templates
select=
"@*|key('unwrap',concat(generate-id(),'|'))"/>
</xsl:copy>
<xsl:for-each
select="key('unwrap',concat(generate-id(),'||'))">
<xsl:apply-templates select="."/>
<para>
<xsl:apply-templates
select=
"
../@*|
key
(
'unwrap',
concat(generate-id(..),'|',generate-id())
)
"/>
</para>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
 
A

aamcfarland

A little something to get the OP started:

Thanks. I'll start playing with this and let you know how it goes.

I'm kind of relieved its so long. I was worried I was missing some
trivial 3 element stylesheet!

Andrew
 
A

aamcfarland

After playing around with this for a bit, I came up with this
stylesheet. There are probably improvements I could make to this, but
I'll be able to faff around with those on my own. Many thanks to
everyone who helped :)

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="unwrap"
match="para/node()[not(self::eek:list)]"
use=
"
concat
(
generate-id(..),'|',
generate-id(preceding-sibling::eek:list[1])
)
"/>
<xsl:key name="unwrap" match="para/olist"
use="concat(generate-id(..),'||')"/>
<xsl:eek:utput indent="yes"/>


<xsl:template match="para">
<p>
<xsl:apply-templates
select=
"@*|key('unwrap',concat(generate-id(),'|'))"/>
</p>
<xsl:for-each
select="key('unwrap',concat(generate-id(),'||'))">
<xsl:apply-templates select="."/>
<p>
<xsl:apply-templates
select=
"
../@*|
key
(
'unwrap',
concat(generate-id(..),'|',generate-id())
)
"/>
</p>
</xsl:for-each>
</xsl:template>

<xsl:template match="olist">
<ol><xsl:apply-templates /></ol>
</xsl:template>

<xsl:template match="li">
<li><xsl:apply-templates /></li>
</xsl:template>

<xsl:template match="document">
<doc><xsl:apply-templates/></doc>
</xsl:template>

</xsl:stylesheet>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top