Модераторы: diadiavova
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Рекурсивный парсинг 
:(
    Опции темы
MasterSID
Дата 4.10.2011, 12:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 24
Регистрация: 15.8.2005

Репутация: нет
Всего: нет



Всем привет!

Решил поизучать XSLT и придумал себе такую задачу. У нас есть XML структура такого вида:
Код

<root>
    <part>
        <title>Заголовок 1</title>
        <text>Некий текст</text>
    </part>
    <part>
        <title>Еще заголовок</title>
        <part>
            <title>Еще один заголовок</title>
            <text>Некий текст</text>
        </part>
        <part>
            <title>Ну и еще заголовок</title>
            <text>Некий текст</text>
        </part>
    </part>
</root>

Т.е. у нас есть дерево, элемент которого представлен тегом part, который обязательно содержит тег title и опционально text. Внутрь part можно вкладывать другой part до бесконечности.

Задача такая: на основе этой структуры данных составить HTML документ, который бы представлял из себя текст, который идет в том же порядке, что и встречается в этом XML и выглядит так:
Код

<a name="some_id"></a>
<h1>Заголовок 1</h1>
<p>Некий текст</p>

<a name="some_id"></a>
<h1>Еще заголовок</h1>

<a name="some_id"></a>
<h2>Еще один заголовок</h2>
<p>Некий текст</p>

<a name="some_id"></a>
<h2>Ну и еще заголовок</h2>
<p>Некий текст</p>


А перед этим текстом шел бы раздел Содержание, который представлял бы представлял из себя нумерованый список рекурсивно вложенный согласно данной структуре и с ссылками на соответсвующие <a name="some_id"></a>.

С задачей на уровне составления отдельно списка содержания и отдельно текста (без линков) я справился. А вот с ссылками проблема. Если бы не было рекурсии, в качестве идентификатора можно было бы использовать индекс нода. getnerate-id() не получается использовать потому, что генерация содержимого и текста идет в разных шаблонах последовательно. Следовательно нужно, если это возможно, генерировать эти два фрагмента одновременно (я это себе представляю только через js и это как-то не вяжется с изучением xslt smile ). Либо нужен способ использовать переменную для инкремента, хотя в спеке написано, что значения переменных изменять нельзя. Если бы было что-то вроде for, то можно было бы сгенерировать что-то вроде index_1_1, index_1_2. Но есть только for-each. Либо есть еще что-то о чем я не догадываюсь.

Заранее благодарен за содействие.

Сейчас код выглядит так:
Код

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output
        media-type="text/html" method="xml"
        omit-xml-declaration="yes"
        doctype-public="-//W3C//DTD HTML 4.01//EN"
        doctype-system="http://www.w3.org/TR/html4/strict.dtd"
    />

    <xsl:template match="/">
        <html>
        <body>
            <xsl:call-template name="contents">
                <xsl:with-param name="node" select="/root" />
            </xsl:call-template>
            <hr/>
            <xsl:call-template name="part">
                <xsl:with-param name="node" select="/root/part" />
            </xsl:call-template>
        </body>
        </html>
    </xsl:template>
    
    <xsl:template name="contents">
        <xsl:param name="node" />
        <xsl:param name="level" select="0" />
        
        <xsl:if test="count($node/part)">
            <ol>
            <xsl:for-each select="$node/part">
                <li>
                    <a>
                        <xsl:attribute name="href">#index_<xsl:value-of select="generate-id()" /></xsl:attribute>
                        <xsl:value-of select="./title" />
                    </a>
                    
                    <xsl:call-template name="contents">
                        <xsl:with-param name="node" select="." />
                        <xsl:with-param name="level" select="$level + 1" />
                    </xsl:call-template>
                </li>
                
            </xsl:for-each>
            </ol>
        </xsl:if>
    </xsl:template>
    
    <xsl:template name="part">
        <xsl:param name="node" />
        <xsl:param name="level" select="0" />
        <xsl:if test="count($node)">
            <div>
                <xsl:for-each select="$node">
                
                    <xsl:variable name="pos" select="position()" />
                    
                    <xsl:for-each select="./*">
                        <xsl:if test="name() = 'title'">
                            <a>
                                <xsl:attribute name="name">index_нет_решения</xsl:attribute>
                                <xsl:choose>
                                     <xsl:when test="$level = 0">
                                        <h2><xsl:value-of select="." /></h2>
                                     </xsl:when>
                                     <xsl:otherwise>
                                        <h3><xsl:value-of select="." /></h3>
                                     </xsl:otherwise>
                                    
                                </xsl:choose>
                            </a>
                        </xsl:if>
                        
                        <xsl:if test="name() = 'part'">
                            <xsl:call-template name="part">
                                <xsl:with-param name="node" select="." />
                                <xsl:with-param name="level" select="$level + 1" />
                            </xsl:call-template>
                        </xsl:if>
                        
                        <xsl:if test="name() = 'text'">
                            <xsl:for-each select="./verse">
                                <p><xsl:value-of select="."/></p>
                            </xsl:for-each>
                        </xsl:if>
                    </xsl:for-each>
                    
                </xsl:for-each>
            </div>
        </xsl:if>
    </xsl:template>
    
</xsl:stylesheet>


PM MAIL   Вверх
diadiavova
Дата 4.10.2011, 15:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Доктор Зло(диагност, настоящий, с лицензией и полномочиями)
****


Профиль
Группа: Модератор
Сообщений: 5821
Регистрация: 14.8.2008
Где: В Коньфпольте

Репутация: 22
Всего: 142



Цитата(MasterSID @  4.10.2011,  13:47 Найти цитируемый пост)
 Если бы было что-то вроде for, то можно было бы сгенерировать что-то вроде index_1_1, index_1_2. 

Ну такие индексы можно и без фора генерировать. Например в шаблоне contents ты передаешь ненужный параметр level, хотя глубину узла можно и без этого подсчитать, в то же время ты мог бы вместо него передавать id для родительского узла, а в самом шаблоне добавлять к нему дополнительный текст. Ну например если у передаваемого узла ид будет index_1, ты к нему добавляешь _1 и т. д. ну и соответственно передаешь уже его далее, как параметр. Можно, конечно, поизголяться и написать шаблон, который по узлу будет вычислять его ид, заданный в таком формате, но это мне кажется уже лишнее.


--------------------
Хочешь получить мудрый совет - читай подписи участников форумов.
Злой доктор Щасзаболит smile
PM   Вверх
MasterSID
Дата 5.10.2011, 03:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 24
Регистрация: 15.8.2005

Репутация: нет
Всего: нет



Ха, спасибо! Как-то в эту сторону у меня мысль не пошла smile Наверное все еще не могу привыкнуть к этим переменным и параметрам...
PM MAIL   Вверх
magelan
Дата 5.10.2011, 16:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


потерял xPath
**


Профиль
Группа: Участник
Сообщений: 393
Регистрация: 5.4.2010

Репутация: 7
Всего: 16



как вариант, если не хочется бодаться с position() передавая его и выстраивая красивые '1_1_1_1' идентификаторы, можешь по мужски брутальненько заюзать generate-id()

именные темпейты используют по необходимости, плохой стиль... обычно они нужны для построения рекурсии, либо как процедура, которая содержит кусок часто повторяющегося кода, в остальных случаях ты просто рассказываешь как обработать тот или иной тег.
твой xsl должен выглядеть как-то так:

Код

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output encoding="utf-8" indent="yes" method="html" />
    
<xsl:template match="/">
    <html>
        <body>
        <ol>
                <xsl:apply-templates mode="contents" />
            </ol>
                <hr/>
                <xsl:apply-templates mode="part" />
        </body>
    </html>
</xsl:template>

<xsl:template match="*" mode="contents" priority="-1"> <!-- для обхода всех элементов, в нашем случае вызывается на root -->
    <xsl:apply-templates mode="contents" />
</xsl:template>
<xsl:template match="part" mode="contents">
    <li>
        <a name="link_{generate-id()}" />
        <xsl:element name="h{count(ancestor-or-self::part)}"> <!-- после h6 мы сгенерим h7, такие штуки лучше делать на css и баловаться именем класса -->
            <xsl:value-of select="title/text()" />
        </xsl:element>
    </li>
    <p>
        <xsl:copy-of select="text/node()"/> <!-- обычно там есть теги -->
    </p>
    <xsl:apply-templates select="part" mode="contents" />
</xsl:template>

<xsl:template match="*" mode="part" priority="-1"> <!-- для обхода всех элементов, в нашем случае вызывается на root -->
    <xsl:apply-templates mode="part" />
</xsl:template>
<xsl:template match="part" mode="part">
    <a href="#link_{generate-id()}">
        <xsl:value-of select="title/text()" />
        <xsl:text> level:[</xsl:text>
        <xsl:value-of select="count( ancestor-or-self::part )" />
        <xsl:text>]</xsl:text>
    </a>
    <br/>
    <xsl:apply-templates select="part" mode="part" />
</xsl:template>
            
</xsl:stylesheet>

PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "XML/XSLT"
diadiavova

Прежде чем опубликовать вопрос, попробуйте воспользоваться поиском - возможно тема уже поднималась.

Также рекомендуем Вам зайти в раздел FAQ ,раздел дополняется и, возможно, там вы увидите готовое решение.

Для ответов на часто задаваемые вопросы существует FAQ раздела. Новости можно публиковать в разделе новостей. Для статей так же есть специальный раздел

Желаем удачи в Вашем деле!


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, diadiavova.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | XML, XSL | Следующая тема »


 




[ Время генерации скрипта: 0.0445 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.