Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > XML, XSL > Получить отфильтрованный набор данных


Автор: infarch 17.8.2015, 15:28
Здравствуйте.

У меня есть xml с такими вот составляющими:
Код

<doclistings>
    <doclist>
        <docfolder>
            <file id="1" />
            <file id="2" />
        </docfolder>
    </doclist>
    <doclist>
        <docfolder>
            <file id="3" />
            <file id="4" />
        </docfolder>
    </doclist>
</doclistings>

<orderfiles>
    <order userid="45" fileid="1" />
    <order userid="45" fileid="2" />
    <order userid="56" fileid="3" />
    <order userid="57" fileid="2" />
</orderfiles>


Задача состоит в следующем: для заданного userid, опираясь на структуру orderfiles, получить c помощью xsl фрагмент структуры doclistings. Например, для userid=45 я должен получить вот такое:
Код

    <doclist>
        <docfolder>
            <file id="1" />
            <file id="2" />
        </docfolder>
    </doclist>


В полученном результате должны присутствовать элементы doclist, docfolder, file. Кто нибудь может подсказать как такое сделать?

Автор: diadiavova 17.8.2015, 15:58
Ну так call-template же. Вот создаешь такой шаблон.
Код

  <xsl:template name="get-doclist">
    <xsl:param name="userid"/>
    <doclist>
      <docfolder>
        <xsl:for-each select="//order[@userid = $userid]">
          <file id="{@fileid}"/>
        </xsl:for-each>
      </docfolder>
    </doclist>
  </xsl:template>
А в  том месте, где надо вставить доклист вызываешь и передаешь юзерид.
Код

      <xsl:call-template  name="get-doclist">
        <xsl:with-param name="userid" select="'45'"/>
      </xsl:call-template>

Автор: infarch 17.8.2015, 16:02
Это не совсем то. Я написал все элементы максимально просто, для примера. На деле же, doclist, docfolder и file имеют достаточно много атрибутов уникальных для них. И если файлы находятся в разных листах и фолдерах, то и в результативной выборке должны быть в них же. Нельзя просто написать лист, надо именно использовать реальный контейнер файла. Это касается и фолдера, и файла.

Автор: diadiavova 17.8.2015, 16:09
infarch, и в чем проблема? Допиши атрибуты куда надо и все. Кроме того, можно передавать любое количество параметров, так что все чего не достает можно добавить. Я тебе пример могу показать только на том материале, который ты предоставил. 

Автор: infarch 18.8.2015, 12:25
Вот, в атаче пример реального xml файла. Попробую объяснить суть задачи. Есть некая виртуальная файловая система (структура doclists). Пользователи (users) заказывают оттуда распечатки файлов. Структура orderfiles показывает какой юзер какие файлы заказал. Теперь самое главное: для каждого пользователя надо создать отдельный кусок структуры doclists, в котором будут только непустые листы и фолдеры с файлами заказанными данным юзером.
Теперь надеюсь понятнее? )

Автор: diadiavova 19.8.2015, 11:20
infarch, Если я правильно понял, то тебе надо, чтобы по сути дела преобразование выполняло немного разные действия, в зависимости от пользователя. Это возможно, если используются параметризированные преобразования. Тут проблема в том, что хотя стандарт и подразумевает возможность создания параметров верхнего уровня, но он ни коим образом не прописывает способа передачи аргументов преобразованию. Способ этот определяется исключительно XSLT-процессором. Так что все зависит от используемой технологии. В общем и целом просто объявляется xsl:param верхнего уровня (то есть не внутри темплейта, а внутри корневого элемента преобразования) и далее программно при преобразовании надо передать аргумент для соответствующего имени параметра. Тогда в  том примере, который я показал не надо передавать userid при вызове шаблона, а вместо этого использовать параметр верхнего уровня.
В частности, если ты создаешь что-то на дотнете, то из списка перегрузок метода https://msdn.microsoft.com/ru-ru/library/system.xml.xsl.xslcompiledtransform.transform(v=vs.110).aspx выбери такой, у которого есть аргумент типа XsltArgumetList, ну и как передавать аргументы там уже в документации все написано. У других технологий правила могут быть другими

Автор: infarch 19.8.2015, 12:21
В конечном итоге я пришел к более другому решению. Идея в том, что для каждого юзера я получаю список айди файлов которые были им заказаны. Потом в каждом фолдере я беру файлы, айди которых входят в этот список. И тогда, если есть хоть один файл, я обрабатываю лист, фолдер, ну и файлы конечно.

Код

<xsl:for-each select="/order/users/user">
    <xsl:variable name="puid" select="@puid" />
    <xsl:variable name="totalprints" select="/order/orderfiles/orderfile[@puid=$puid]" />
    <xsl:variable name="fids" select="$totalprints/@fileid" /> <!-- Take the ordered files IDs -->
    
    <!-- Loop through files -->
    <xsl:for-each select="/order/doclists/doclist">
        <xsl:variable name="listname" select="@name" />
        
        <xsl:for-each select="docfolder">
            <xsl:variable name="foldername" select="@name" />
            
            <xsl:variable name="localfiles" select="file[@ID=$fids]" /> <!-- Take files -->
            
            <xsl:if test="count($localfiles) &gt; 0">
                
                <!--
                    The user ordered at least one file in this list/folder,
                    so we copy the structure and populate it by the files.
                -->
                
            </xsl:if>
                
        </xsl:for-each>
    </xsl:for-each>
        
</xsl:for-each>


Я не знал, что в выражении типа [@ID=$fids] xsl позволяет использовать список значений. Но попробовал, и получилось.

Автор: diadiavova 19.8.2015, 12:47
infarch, так тебе группировка нужна была? 

Автор: infarch 19.8.2015, 15:30
Наверно, можно сказать и так. Тут я, увы, не силен в терминологии. Но группировать надо было по данным, приходящим из совсем другого набора (не doclists, a orderfiles).

Автор: diadiavova 19.8.2015, 15:50
Цитата(infarch @  19.8.2015,  15:30 Найти цитируемый пост)
Тут я, увы, не силен в терминологии.

Да терминология простая: если тебе надо из документа получить данные по одному критерию(например пользователю), это фильтрация, как и было написано в названии темы, а если тебе надо вывести данные по всем пользователям в один документ, при этом разместив данные по каждому в отдельную группу - это группировка. 

Автор: infarch 19.8.2015, 16:05
diadiavova, извиняй, запутался с терминами ) Ну как ни назови, я это сделал! Теперь знаю xsl немного больше. А не посоветуешь нормальную книгу или сайт по нему? А то все описания что я находил на редкость неудобоваримы.

Автор: diadiavova 19.8.2015, 16:13
Цитата(infarch @  19.8.2015,  16:05 Найти цитируемый пост)
А не посоветуешь нормальную книгу или сайт по нему?

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)