Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.22.100.180] |
|
Сообщ.
#1
,
|
|
|
Добрый день, форумчане!
Только зарегистрировался, встречайте новичка! Собственно, интересный, на мой взгляд, сабж. Есть xml-файл с примерно следующей разметкой: <?xml version="1.0" encoding="UTF-8"?> <intermediate name="Simple" package="simple"> <type wireName="point" name="Point" package="simple.data" isAbstract="no" base="Element"> <field name="lat" type="double" isOptional="yes" isArray="no" wireName="lat" isSimple="yes"/> <field name="lon" type="double" isOptional="yes" isArray="no" wireName="lon" isSimple="yes"/> <field name="lur" type="double" isOptional="yes" isArray="no" wireName="lur" isSimple="yes"/> <field name="lit" type="double" isOptional="no" isArray="no" wireName="lit" isSimple="yes"/> </type> </intermediate> Необходимо из него вывести текстовый файл с примерно таким содержанием: - point: [lat, lon, lur, lit] , НО!!! Не совсем так. Строчек point должно быть столько, сколько есть всевозможных комбинаций опциональных параметров (isOptional="yes") + обязательных (isOptional="no"). То есть точно вывод должен быть такой: - point: [lat, lon, lur, lit] - point: [lon, lur, lit] - point: [lat, lur, lit] - point: [lat, lon, lit] - point: [lat, lit] - point: [lon, lit] - point: [lur, lit] или в другом порядке, это неважно. Строки потом можно отформатировать. Последнее, что пришло в голову - это следующее: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:navbuilder="http://www.navbuilder.com"> <xsl:output method="text" omit-xml-declaration="yes" indent="yes" /> <xsl:strip-space elements="*" /> <xsl:template match="/"> <xsl:apply-templates /> <xsl:call-template name="recursive" /> </xsl:template> <xsl:template name = "recursive" match="package"> <xsl:apply-templates /> </xsl:template> <xsl:template match="type"> <xsl:text> - </xsl:text> <xsl:value-of select="@wireName" /> <xsl:text>: [</xsl:text> <xsl:for-each select="field"> <xsl:sort select="@wireName" data-type="text" order="ascending"/> <xsl:if test="@isSimple = 'yes'"> <xsl:choose> <xsl:when test="@isOptional = 'yes'"> <xsl:value-of select="@wireName" /> <xsl:text>]</xsl:text> <xsl:text>
</xsl:text> <xsl:call-template name="recursive" /> </xsl:when> <xsl:otherwise> <xsl:value-of select="@wireName" /> <xsl:if test="position() != last()"> <xsl:text>, </xsl:text> </xsl:if> </xsl:otherwise> </xsl:choose> </xsl:if> </xsl:for-each> <xsl:text>]</xsl:text> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet> В этом шаблоне я заодно сортирую атрибуты в строчке. Но это все равно должным образом не работает. Кто-нибудь может помочь? Вообще, обход всевозможных вариантов средством xsl возможен? Бьюсь с этим уже неделю. Боюсь, что скоро мысли иссякнут. П.С. Сильно не пинайте, если шаблон кривой. Я в этом новичок, но к сожалению, приходится уже решать нетривиальные задачи. |
Сообщ.
#2
,
|
|
|
<?xml version="1.0" encoding="UTF-8"?> <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="/"> <xsl:apply-templates select="intermediate/type"/> </xsl:template> <xsl:template match="type"> <xsl:apply-templates select="field[1]" mode="recursive-combine"> <xsl:with-param name="wire-name" select="@wireName"/> </xsl:apply-templates> </xsl:template> <xsl:template match="field" mode="recursive-combine"> <xsl:param name="accumulated-string"/> <xsl:param name="wire-name"/> <xsl:param name="hidden" select="false()"/> <!-- в первый раз никто не скрыт, то есть опциональный field будет выведен --> <xsl:variable name="is-last" select="not(following-sibling::field)"/> <xsl:variable name="comma" select="substring(', ', 1 div (not($hidden) and not($is-last)) )"/> <!-- это - сильное колдунство --> <xsl:variable name="new-accumulated" select="concat($accumulated-string, @name[not($hidden)], $comma)"/> <xsl:if test="not($is-last)"> <xsl:apply-templates select="following-sibling::field[1]" mode="recursive-combine"> <xsl:with-param name="accumulated-string" select="$new-accumulated"/> <xsl:with-param name="wire-name" select="$wire-name"/> </xsl:apply-templates> </xsl:if> <xsl:apply-templates select="current()[$is-last]" mode="output"> <xsl:with-param name="accumulated-string" select="$new-accumulated"/> <xsl:with-param name="wire-name" select="$wire-name"/> </xsl:apply-templates> <xsl:apply-templates select="current()[@isOptional='yes'][not($hidden)]" mode="recursive-combine"> <xsl:with-param name="hidden" select="true()"/> <xsl:with-param name="accumulated-string" select="$accumulated-string"/> <xsl:with-param name="wire-name" select="$wire-name"/> </xsl:apply-templates> </xsl:template> <xsl:template match="*" mode="recursive-combine"/> <!-- жрёт всё подряд, потому что я люблю пихать условия в виде current()[$is-last]. можно заменить на именованый шаблон и xsl:if --> <xsl:template match="*" mode="output"> <xsl:param name="accumulated-string"/> <xsl:param name="wire-name"/> <!-- вывод в таком виде, потому что на выходе всё-таки XML. переделывай как хочешь. --> <entry value="{concat('- ', $wire-name, ': [', $accumulated-string, ']')}"/> </xsl:template> </xsl:stylesheet> По-хорошему надо оперировать не строками, а node-set'ами, запятые тыкать в процессе вывода, а не обхода. Это тебе будет домашняя работа - переделать недолго. |
Сообщ.
#3
,
|
|
|
ss, большое спасибо! Прям огромное. То, что нужно.
Переделал вывод в текстовый формат - теперь совсем хорошо. Для этого заменил последний темплейт на: <xsl:template match="*" mode="output"> <xsl:param name="accumulated-string"/> <xsl:param name="wire-name"/> <xsl:value-of select="concat('- ', $wire-name, ': [', $accumulated-string, ']', '
')" /> </xsl:template> Ну и вначале поставил тип вывода на текстовый. В общем, это было несложно . Еще вопрос: а как теперь выводить только те элементы, у которых isSimple = "yes"? Куда надо добавить это условие? Всего третий день изучаю xsl. Как же это сложно. Мне кажется, что я никогда это не пойму . И еще: может дадите какие-нибудь рекомендации по изучению? Чувствую, что андроид-программисту по текущей ситуации еще долго придется разбираться в xsl . Книжки, задания, справочники? Кто по чему изучал? Спасибо! Добавлено ss, скажи пожалуйста, а нужна зачем строчка <xsl:template match="*" mode="recursive-combine"/> Как я понимаю, этот шаблон ничего не делает. Зачем тогда он? |
Сообщ.
#4
,
|
|
|
Цитата tomboy1 @ как теперь выводить только те элементы, у которых isSimple = "yes" <xsl:template match="field" mode="recursive-combine"> <xsl:template match="field[@isOptional='yes']" mode="recursive-combine"> Цитата tomboy1 @ Это называется "высокий порог вхождения". Входи - понравится Всего третий день изучаю xsl. Как же это сложно. Мне кажется, что я никогда это не пойму Добавлено Цитата tomboy1 @ Он отлавливает остатки.Как я понимаю, этот шаблон ничего не делает. Зачем тогда он? Даже если ты такой шаблон не напишешь, "всё остальное", что ты явно не match'ишь своими шаблонами, будет поймано built-in шаблоном (одним из), а они только и делают, что в итоге выводят всё (что могут) в output. Как правило, такой вывод не нужен. |
Сообщ.
#5
,
|
|
|
Тогда скорее на
<xsl:template match="field[@isSimple='yes']" mode="recursive-combine"> а не на <xsl:template match="field[@isOptional='yes']" mode="recursive-combine"> Идею понял. Спасибо еще раз! |
Сообщ.
#6
,
|
|
|
Ну да. Невнимательно прочитал.
|
Сообщ.
#7
,
|
|
|
Ан нет, не совсем так.
Если элемент isSimple = 'yes', то он выводится. Если isSimple = 'no', то он не выводится, но вместо него выводятся пустые скобки. Т.е. для xml: <?xml version="1.0" encoding="UTF-8"?> <intermediate name="simple" package="protocol"> <type wireName="point" name="Point"> <field name="lat" type="double" isOptional="yes" isArray="no" wireName="lat" isSimple="yes"/> <field name="lon" type="double" isOptional="yes" isArray="no" wireName="lon" isSimple="yes"/> <field name="lur" type="double" isOptional="yes" isArray="no" wireName="lur" isSimple="yes"/> <field name="lit" type="double" isOptional="no" isArray="no" wireName="lit" isSimple="yes"/> </type> <type wireName="box" name="Box" > <field name="rightCornerPoint" type="Point" isOptional="no" wireName="point" isSimple="no"/> <field name="leftCornerPoint" type="Point" isOptional="no" wireName="point" isSimple="no"/> </type> </intermediate> вывод должен быть таким: - point: [lat, lon, lur, lit] - point: [lat, lon, lit] - point: [lat, lur, lit] - point: [lat, lit] - point: [lon, lur, lit] - point: [lon, lit] - point: [lur, lit] - point: [lit] - box: [] а не таким: - point: [lat, lon, lur, lit] - point: [lat, lon, lit] - point: [lat, lur, lit] - point: [lat, lit] - point: [lon, lur, lit] - point: [lon, lit] - point: [lur, lit] - point: [lit] Если добавить условие [@isOptional='yes']в строчку <xsl:template match="field" mode="recursive-combine"> то эти поля просто будут пропускаться. Нужно, чтобы эти поля не пропускались, но выводились пустые скобки. |
Сообщ.
#8
,
|
|
|
<xsl:variable name="comma" select="substring(', ', 1 div (not($hidden) and not($is-last)) )"/> <xsl:variable name="new-accumulated" select="concat($accumulated-string, @name[not($hidden)], $comma)"/> <xsl:variable name="is-simple" select="@isSimple='yes'"/> <xsl:variable name="comma" select="substring(', ', 1 div (not($hidden) and not($is-last) and $is-simple) )"/> <xsl:variable name="new-accumulated" select="concat($accumulated-string, @name[not($hidden) and $is-simple], $comma)"/> Не проверял. |
Сообщ.
#9
,
|
|
|
Сделал так (последний шаблон):
<!-- output template --> <xsl:template match="field" mode="output"> <xsl:param name="accumulated-string"/> <xsl:param name="wire-name"/> <xsl:choose> <xsl:when test="@isSimple = 'yes'"> <!-- output string --> <xsl:value-of select="concat('- ', $wire-name, ': [', $accumulated-string, ']', '
')" /> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat('- ', $wire-name, ': [', ']', '
')" /> </xsl:otherwise> </xsl:choose> </xsl:template> Вроде работает |
Сообщ.
#10
,
|
|
|
Цитата tomboy1 @ Неправильно. Вроде работает |
Сообщ.
#11
,
|
|
|
Цитата ss @ Неправильно. Да, и правда неправильно. Цитата ss @ Не проверял. В этом случае выводятся несколько элементов, у которых атрибуты isOptional="yes" и "isSimple=no". Т.е. для xml: <type wireName="content-path" name="ContentPath"> <field name="startPoint" type="Point" isOptional="yes" wireName="start-point" isSimple="no"/> <field name="endPoint" type="Point" isOptional="yes" wireName="end-point" isSimple="no"/> <field name="pathFileID" type="String" isOptional="no" wireName="id" isSimple="yes"/> <field name="routeSplinePacked" type="BinaryData" isOptional="no" wireName="route-spline-packed" isSimple="yes"/> </type> вывод будет таким: - content-path: [pathFileID, routeSplinePacked] - content-path: [pathFileID, routeSplinePacked] - content-path: [pathFileID, routeSplinePacked] - content-path: [pathFileID, routeSplinePacked] а должен быть таким: - content-path: [pathFileID, routeSplinePacked] |
Сообщ.
#12
,
|
|
|
<xsl:apply-templates select="current()[@isOptional='yes'][not($hidden)]" mode="recursive-combine"> <xsl:apply-templates select="current()[@isOptional='yes'][not($hidden)][$is-simple]" mode="recursive-combine"> Добавлено та ещё каша |
Сообщ.
#13
,
|
|
|
Цитата ss @ та ещё каша Да уж. ss, большое спасибо! Теперь, вроде, все правильно. |
Сообщ.
#14
,
|
|
|
Ан нет, и сейчас немного неправильно.
Смысл вот в чем. Если последний атрибут сделать опциональным, то будет лишняя запятая в конце. Т.е. вот для такой xml: <type wireName="point"> <field wireName="lat" isOptional="yes" isSimple="yes"/> <field wireName="lit" isOptional="no" isSimple="yes"/> <field wireName="lon" isOptional="yes" isSimple="yes"/> <field wireName="lur" isOptional="yes" isSimple="yes" name="lur"/> </type> - point: [lat, lit, lon, lur] - point: [lat, lit, lon, ] - point: [lat, lit, lur] - point: [lat, lit, ] - point: [lit, lon, lur] - point: [lit, lon, ] - point: [lit, lur] - point: [lit, ] Есть запятая, например, во второй строчке перед закрывающейся скобкой. Она лишняя. Чтобы ее убрать, написал такую переменную: После строчки <xsl:variable name="is-last" select="not(following-sibling::field)"/> <xsl:variable name="is-last-optional" select="(not($is-last) and following-sibling::field[$is-last][@isOptional='yes'][not($hidden)])"/> <xsl:variable name="comma" select="substring(', ', 1 div (not($hidden) and not($is-last) and $is-simple and is-last-optional))"/> Не работает. Он вообще не ставит запятые. По логике, если мы имеем предпоследний элемент, а последний у нас опциональный и скрытый, то мы не должны ставить запятую. Вроде так и написал, но не работает. ss, добрый человек, помоги |
Сообщ.
#15
,
|
|
|
Помогу послезавтра.
|
Сообщ.
#16
,
|
|
|
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:template match="/"> <xsl:apply-templates select="intermediate/type"/> </xsl:template> <xsl:template match="type"> <xsl:apply-templates select="field[1]" mode="recursive-combine"> <xsl:with-param name="wire-name" select="@wireName"/> </xsl:apply-templates> </xsl:template> <xsl:template match="field[@isSimple='yes']" mode="recursive-combine"> <xsl:param name="accumulated" select="node()"/> <xsl:param name="wire-name"/> <xsl:param name="hidden" select="false()"/> <!-- в первый раз никто не скрыт, то есть опциональный field будет выведен --> <xsl:variable name="is-last" select="not(following-sibling::field)"/> <xsl:variable name="is-simple" select="@isSimple='yes'"/> <xsl:variable name="new-accumulated" select="$accumulated | current()[not($hidden) and $is-simple]"/> <xsl:if test="not($is-last)"> <xsl:apply-templates select="following-sibling::field[1]" mode="recursive-combine"> <xsl:with-param name="accumulated" select="$new-accumulated"/> <xsl:with-param name="wire-name" select="$wire-name"/> </xsl:apply-templates> </xsl:if> <xsl:apply-templates select="current()[$is-last]" mode="output"> <xsl:with-param name="accumulated" select="$new-accumulated"/> <xsl:with-param name="wire-name" select="$wire-name"/> </xsl:apply-templates> <xsl:apply-templates select="current()[@isOptional='yes'][not($hidden)][$is-simple]" mode="recursive-combine"> <xsl:with-param name="hidden" select="true()"/> <xsl:with-param name="accumulated" select="$accumulated"/> <xsl:with-param name="wire-name" select="$wire-name"/> </xsl:apply-templates> </xsl:template> <xsl:template match="*" mode="recursive-combine"/> <xsl:template match="field" mode="output"> <xsl:param name="accumulated"/> <xsl:param name="wire-name"/> <xsl:value-of select="concat('- ', $wire-name, ': [')"/> <xsl:for-each select="$accumulated"> <xsl:value-of select="concat( substring(', ', 1 div (not(position()=1)) ), @name )" /> </xsl:for-each> <xsl:value-of select="concat(']', '
')" /> </xsl:template> </xsl:stylesheet> 1. нафиг чойз в выводе, если до вывода доходят только простые поля? 2. переделал на ноде-сеты (смотри внимательно на парметр accumulated), потому нужно допилить под твой парсер - каждый по-своему с сетами работает. |
Сообщ.
#17
,
|
|
|
В строке 15:
<xsl:template match="field[@isSimple='yes']" mode="recursive-combine"> убрал [@isSimple='yes'], чтобы были выведены пустые элементы. Т.е. для <type wireName="box" name="Box" > <field name="rightCornerPoint" type="Point" isOptional="no" wireName="point" isSimple="no"/> <field name="leftCornerPoint" type="Point" isOptional="no" wireName="point" isSimple="no"/> </type> - box: [] Все остальное, вроде, правильно. Еще раз большое спасибо!!! |