#xml #xslt
#xml #xslt
Вопрос:
Я пытаюсь преобразовать ужасный XML со встроенным форматированием richtext в HTML. Я нашел способ определить начало маркированного списка на основе атрибута в узле предыдущего родственного узла, но я не могу понять, как завершить маркированный список. Проблема в том, что атрибуты каждого блока текста определяются либо в узле предыдущего родственного элемента, либо в первом абзаце в серии.
У меня есть следующий XML:
<?xml version="1.0" encoding="UTF-8"?>
<document>
<item>
<richtext>
<pardef/>
<par def='20'><run>This is the first </run><run>paragraph of the preamble.</run></par>
<par><run>This is the second paragraph of the </run><run>preamble.</run></par>
<pardef list='bullet'/>
<par def='21'><run>This is the </run><run>first bullet.</run></par>
<par><run>This is the second </run><run>bullet.</run></par>
<par def='20'><run>This is the first </run><run>paragraph of the conclusion.</run></par>
<par><run>This is the second paragraph of the </run><run>conclusion.</run></par>
</richtext>
</item>
</document>
Я хочу следующий вывод:
<p>This is the first paragraph of the preamble.</p>
<p>This is the second paragraph of the preamble.</p>
<ul>
<li>This is the first bullet.</li>
<li>This is the second bullet.</li>
</ul>
<p>This is the first paragraph of the conclusion.</p>
<p>This is the second paragraph of the conclusion.</p>
У меня есть следующий XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:key name="key-for-par" match="document/item/richtext/par" use="generate-id(preceding-sibling::pardef[1])"/>
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="document/item/richtext/pardef" />
</xsl:template>
<xsl:template match="pardef[@list = 'bullet']">
<ul>
<xsl:for-each select="key('key-for-par', generate-id(.))">
<li>
<xsl:value-of select="run" separator=""/>
</li>
</xsl:for-each>
</ul>
</xsl:template>
<xsl:template match="pardef[not(@list)]">
<xsl:for-each select="key('key-for-par', generate-id(.))">
<p>
<xsl:value-of select="run" separator=""/>
</p>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Комментарии:
1. Разве не должен быть
<pardef/>
узел абзаца непосредственно перед заключением?2. Да, они должны быть, но, к сожалению, их нет. Я работаю с XML-дампом из устаревшей системы Lotus Notes, и мне приходится работать с тем, что у меня есть.
3. Итак, перед каждым заключительным абзацем нет
pardef
? Как вы можете отличить пункты маркера от заключительного абзаца? Просматривая содержимое?4. кажется, это не ясный или безопасный способ определить, что «par» является списком маркеров … если только «def» не имеет значения, т.Е. «21» означает список маркеров? в этом случае вы можете вообще игнорировать pardef и использовать значение «def».
5. Я могу определить, какие абзацы являются маркерами, посмотрев на исходную систему, из которой был извлечен XML-дамп. Первоначально я думал, что pardef list=bullet будет достаточно, но при дальнейшем анализе кажется, что par def=21 указывает на маркерную точку.
Ответ №1:
Согласно комментариям, предполагая, что def = 21 является началом маркированного списка, тогда мы можем игнорировать ‘pardef’ и, используя ‘par’ в конструкции выбора, мы можем определить, когда писать ul, li, p и соответствующие им закрывающие теги. Я попробовал это на примере и даже на некоторых его вариантах и работает нормально:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="document/item/richtext/par" />
</xsl:template>
<xsl:template match="par">
<xsl:if test="@def and preceding-sibling::par[@def][1][@def='21']"><xsl:text disable-output-escaping="yes">amp;</ulamp;></xsl:text></xsl:if>
<xsl:choose>
<xsl:when test="@def=21 or (not(@def) and preceding-sibling::par[@def][1][@def='21'])">
<xsl:if test="@def=21"><xsl:text disable-output-escaping="yes">amp;<ulamp;></xsl:text></xsl:if>
<li>
<xsl:for-each select="run">
<xsl:value-of select="text()" separator=""/>
</xsl:for-each>
</li>
</xsl:when>
<xsl:when test="not(@def=21)">
<p>
<xsl:for-each select="run">
<xsl:value-of select="text()" separator=""/>
</xsl:for-each>
</p>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>