Преобразование XSL из версии 2.0

У меня проблема с преобразованием XSLT 2.0 в XSLT 3.0. Я хочу использовать возможности потоковой передачи XSLT 3.0. Я борюсь с использованием тегов <xsl:stream> и <xsl:mode>, поскольку XSLT 3.0 имеет несколько ограничений. При использовании возможностей потоковой передачи XSLT 3.0 возникают следующие ограничения:

  1. «Родственные узлы и родственный предок недоступны».
  2. «Вы можете посещать дочерние узлы только один раз»
  3. «У вас есть доступ только к текущим атрибутам элемента и объявлению пространства имен»

Как я могу преодолеть эти ограничения? Кто-нибудь может мне помочь, пожалуйста!

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

         <xsl:stream href="SampleInput3_0.xml">
            <xsl:for-each select="copy-of(ns0:ORM_O01/ns0:ORM_O01.PATIENT/ns0:PID)">
               <Patient>
                  <ucfd:Name>
                     <xsl:variable name="varFirst_Name" as="node()" select="ns0:PID.5/ns0:XPN.2[not((translate(string(@xsi:nil), 'true ', '1') = '1'))]" />
                     <xsl:variable name="varLast_Name" as="node()" select="ns0:XPN.1/ns0:FN.1[not((translate(string(@xsi:nil), 'true ', '1') = '1'))]" />
                     <xsl:attribute name="value" namespace="" select="concat(string($varFirst_Name), string(varLast_Name))" />
                  </ucfd:Name>
                  <ucfd:FirstName value="{ns0:PID.5/ns0:XPN.2[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                  <ucfd:LastName value="{ns0:PID.5/ns0:XPN.1/ns0:FN.1}" />
                  <ucfd:MiddleName value="{ns0:PID.5/ns0:XPN.3[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                  <ucfd:Prefix value="{ns0:PID.5/ns0:XPN.5[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                  <ucfd:Suffix value="{ns0:PID.5/ns0:XPN.4[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                  <ucfd:Identification>
                     <ucfd:TaxIdentifier>
                        <ucfd:Type value="{ns0:PID.18/ns0:CX.5[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                        <ucfd:Identifier value="{ns0:PID.18/ns0:CX.1}" />
                     </ucfd:TaxIdentifier>
                  </ucfd:Identification>
                  <xsl:for-each select="ns0:PID.11">
                     <ucfd:Address>
                        <xsl:if test="exists(@Type)">
                           <ucfd:Type value="{ns0:XAD.7[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                        </xsl:if>
                        <ucfd:City value="{ns0:XAD.3[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                        <ucfd:State value="{ns0:XAD.4[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                        <ucfd:PostalCode value="{ns0:XAD.5[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                        <ucfd:Country value="{ns0:XAD.6[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                        <ucfd:County value="{ns0:XAD.9[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                     </ucfd:Address>
                  </xsl:for-each>
                  <ucfd:Contact>
                     <ucfd:Role value="{ns0:PID.13/ns0:XTN.2[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                     <ucfd:Type value="{ns0:PID.13/ns0:XTN.3[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                     <xsl:for-each select="ns0:PID.13">
                        <!--<xsl:variable name="var9_current" as="node()" select="."/>-->
                        <ucfd:CommunicationNumber>
                           <xsl:if test="exists(@Type)">
                              <ucfd:Type>
                                 <xsl:sequence select="()" />
                              </ucfd:Type>
                           </xsl:if>
                           <ucfd:Identifier>
                              <!--<xsl:variable name="var8_current" as="node()" select="ns0:XTN.5[not((translate(string(@xsi:nil), 'true ', '1') = '1'))]"/>
                                <xsl:variable name="var7_current" as="node()" select="ns0:XTN.6[not((translate(string(@xsi:nil), 'true ', '1') = '1'))]"/>
                                <xsl:variable name="var6_current" as="node()" select="ns0:XTN.7[not((translate(string(@xsi:nil), 'true ', '1') = '1'))]"/>
                                <xsl:variable name="var5_current" as="node()" select="ns0:XTN.8[not((translate(string(@xsi:nil), 'true ', '1') = '1'))]"/>
                                <xsl:variable name="var4_current" as="node()" select="ns0:XTN.10[not((translate(string(@xsi:nil), 'true ', '1') = '1'))]"/>
                                <xsl:variable name="var3_current" as="node()" select="ns0:XTN.11[not((translate(string(@xsi:nil), 'true ', '1') = '1'))]"/>
                                <xsl:attribute name="value" namespace="" select="concat(concat(concat(concat(concat(string($var8_current), string($var7_current)), string($var6_current)), string($var5_current)), string($var4_current)), string($var3_current))"/> -->
                           </ucfd:Identifier>
                        </ucfd:CommunicationNumber>
                     </xsl:for-each>
                  </ucfd:Contact>
                  <ucfd:Demographics>
                     <ucfd:BirthDate>
                        <xsl:sequence select="()" />
                     </ucfd:BirthDate>
                     <ucfd:BirthSequenceNumber>
                        <xsl:for-each select="ns0:ORM_O01/ns0:ORM_O01.PATIENT/ns0:PID/ns0:PID.25">
                           <xsl:attribute name="value" namespace="">
                              <xsl:if test="not((translate(string(@xsi:nil), 'true ', '1') = '1'))">
                                 <xsl:sequence select="xs:string(xs:integer(string(.)))" />
                              </xsl:if>
                           </xsl:attribute>
                        </xsl:for-each>
                     </ucfd:BirthSequenceNumber>
                     <ucfd:DeathDate>
                        <xsl:sequence select="()" />
                     </ucfd:DeathDate>
                     <ucfd:Gender value="ns0:PID.8[not((translate(string(@xsi:nil), 'true ', '1') = '1'))]" />
                     <ucfd:MaritalStatus value="{ns0:PID.16/ns0:CE_0002.1[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                     <ucfd:CitizenshipStatus value="{ns0:PID.26[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                     <ucfd:RaceOrEthnicity>
                        <ucfd:Race value="{ns0:PID.10/ns0:CE_0005.1[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                     </ucfd:RaceOrEthnicity>
                     <ucfd:Religion value="{ns0:PID.17/ns0:CE_0006.2[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                  </ucfd:Demographics>
                  <ucfd:Language>
                     <ucfd:LanguageCode value="{ns0:PID.15/ns0:CE_0296.1[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                     <ucfd:Description value="{ns0:PID.15/ns0:CE_0296.2[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                     <ucfd:UseIndicator value="{ns0:PID.15/ns0:CE_0296.3[not(translate(@xsi:nil, 'true ', '1') = '1')]}" />
                  </ucfd:Language>
               </Patient>
            </xsl:for-each>
         </xsl:stream>
      </MemberRecord>
   </xsl:template>
</xsl:stylesheet>

person user3769767    schedule 24.06.2014    source источник
comment
@user3769767 user3769767 Если в этом коде есть другая личная информация, кроме доменов, укажите это. Если он абсолютно не может появиться, мы можем записать ревизии, где он виден. Но удаление всего кода неприемлемо, потому что это имеет отношение к ответу на вопрос. Как видно из ответа ниже, пользователь анонимизировал все доменные имена. Есть ли другая информация, которую не следует отображать?   -  person animuson    schedule 30.07.2014
comment
Да, есть другая конфиденциальная информация. Я замаскировал доменное имя ранее в июне, но теперь я хотел бы удалить полное пространство имен xml, если нельзя удалить весь xsl. Вы просто публикуете код в вопросе, так как я больше не могу его видеть, и я отредактирую его, чтобы там не было конфиденциальной информации. Кроме того, я хотел бы отредактировать ответы, отправленные другим пользователем, поскольку маскировки домена недостаточно.   -  person user3769767    schedule 30.07.2014
comment
Вы можете редактировать предыдущую версию из истории изменений. Просто выберите, с какой версии вы хотите начать, и нажмите кнопку редактировать на этой версии. Достаточно ли просто анонимизировать все URL-адреса в коде (т. е. заменить их на ..., чтобы указать, что они существуют, но не отображаются)?   -  person animuson    schedule 30.07.2014
comment
Я удалил URL-адреса из XSL. Я также хочу удалить URL-адреса в ответах, опубликованных другим пользователем. Замена URL на ... также будет достаточной. Как я могу редактировать ответы?   -  person user3769767    schedule 30.07.2014
comment
Я отредактировал ответ для вас. Теперь все выглядит нормально?   -  person animuson    schedule 30.07.2014
comment
Привет. Да, теперь все выглядит нормально.. Спасибо! Кроме того, можно ли скрыть этот разговор, так как я не хочу, чтобы его видели публично? Буду очень признателен, если это удастся сделать.   -  person user3769767    schedule 30.07.2014


Ответы (1)


Я думаю, вам следует следовать подходу, описанному в http://saxonica.com/documentation/index.html#!sourcedocs/streaming/burst-mode-streaming, поэтому в соответствии с

<xsl:stylesheet 
  version="3.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:ns0="..."
  xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="..." xmlns:ccfd="..." xmlns:hcfd="..." xmlns:ucf="..." xmlns:ucfd="..."
  exclude-result-prefixes="ns0 xs">

<xsl:output method="xml" encoding="UTF-8" byte-order-mark="no" indent="yes"/>

<!-- start with this named template -->
<xsl:template name="main">
  <MemberRecord xsi:schemaLocation="...">
    <Patient>
      <xsl:stream href='employees.xml'>
        <xsl:apply-templates select="copy-of(ns0:ORM_O01/ns0:ORM_O01.PATIENT/ns0:PID)"/>
      </xsl:stream>
    </Patient>
  </MemberRecord>
</xsl:template>

<xsl:template match="ns0:PID">
  <ucfd:Name value="{...}"/>
  <ucfd:FirstName value="{ns0:PID.5/ns0:XPN.2[not(translate(@xsi:nil, 'true ', '1') = '1'))]}"/>
  <ucfd:LastName value="{ns0:PID.5/ns0:XPN.1/ns0:FN.1}"/>
  <!-- compute the other elements and attributes here as above -->
</xsl:template>

</xsl:stylesheet>

Это должно показать подход, вам нужно будет добавить код для остальных элементов, таких как отчество, префикс и так далее. Я также попытался упростить вычисление атрибута value в некоторых случаях, я думаю, что то же самое можно сделать и для других случаев, но я думаю, что вы должны задать это в другом вопросе, где вы предоставляете подробную информацию о структуре XML и значениях, которые вы хотите вычислить, все эти вложенные for-each для создания одного атрибута value кажутся немного запутанными.

Также обратите внимание, что я следовал вашему опубликованному коду при создании одного элемента Patient, однако я ожидал, что элемент ns0:ORM_O01.PATIENT сопоставляется с Patient, так что больше по строкам

<xsl:stylesheet 
  version="3.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:ns0="..."
  xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="..." xmlns:ccfd="..." xmlns:hcfd="..." xmlns:ucf="..." xmlns:ucfd="..."
  exclude-result-prefixes="ns0 xs">

<xsl:output method="xml" encoding="UTF-8" byte-order-mark="no" indent="yes"/>

<!-- start with this named template -->
<xsl:template name="main">
  <MemberRecord xsi:schemaLocation="...">

      <xsl:stream href='employees.xml'>
        <xsl:apply-templates select="copy-of(ns0:ORM_O01/ns0:ORM_O01.PATIENT)"/>
      </xsl:stream>
    </Patient>
  </MemberRecord>
</xsl:template>

<xsl:template match="ns0:ORM_O01.PATIENT">
  <Patient>
    <xsl:apply-templates/>
  </Patient>
</xsl:template>

<xsl:template match="ns0:PID">
  <ucfd:Name value="{...}"/>
  <ucfd:FirstName value="{ns0:PID.5/ns0:XPN.2[not(translate(@xsi:nil, 'true ', '1') = '1'))]}"/>
  <ucfd:LastName value="{ns0:PID.5/ns0:XPN.1/ns0:FN.1}"/>
  <!-- compute the other elements and attributes here as above -->
</xsl:template>

</xsl:stylesheet>
person Martin Honnen    schedule 24.06.2014
comment
Большое спасибо Мартин! Это действительно помогло мне... и вы правы, элемент ns0:ORM_O01.PATIENT сопоставлен с пациентом и содержит другие дочерние элементы, отличные от PID. Я буду использовать второй XSL, который вы предоставили. - person user3769767; 25.06.2014
comment
Кроме того, я вставил полный XSL пациента в свой вопрос выше. Должен ли я следовать тому же подходу и для остальных элементов (идентификация, адрес, демография, язык и т. д.)? Заранее большое спасибо !! - person user3769767; 25.06.2014
comment
Что касается потоковой передачи, я предполагаю, что на входе слишком много ns0:ORM_O01.PATIENT для построения полного дерева из них с традиционной обработкой XSLT 2.0, но один ns0:ORM_O01.PATIENT прекрасно вписывается в память, и обработке требуется только доступ к его дочерним элементам или потомкам. Таким образом, с <xsl:stream href='employees.xml'><xsl:apply-templates select="copy-of(ns0:ORM_O01/ns0:ORM_O01.PATIENT)"/> процессор XSLT 3.0 будет считывать в потоковом режиме последовательно шаг за шагом любой элемент ns0:ORM_O01/ns0:ORM_O01.PATIENT, а затем позволит нам обычную обработку XSLT на копии. - person Martin Honnen; 25.06.2014
comment
Таким образом, вы сможете использовать любую обработку с шаблонами или, если необходимо, для каждого дочернего элемента и потомков элементов ns0:ORM_O01/ns0:ORM_O01.PATIENT, таких как ns0:PID, без необходимости думать об ограничениях потоковой передачи. - person Martin Honnen; 25.06.2014
comment
Замечательно ! Всего один вопрос. Я могу показаться глупым, но ‹xsl:stream href=employees.xml›. Нужно ли мне предоставлять файл employee.xml для потоковой передачи, или это просто фиктивный файл, и это не имеет значения? - person user3769767; 25.06.2014
comment
Я предполагаю, что employees.xml будет вашим очень большим входным файлом, который вы хотите обрабатывать в потоковом режиме. Написанный мной код следует вызывать с помощью Saxon, предоставив исходный шаблон в качестве отправной точки, используя параметр командной строки it:main. Я исправил XSLT в своем ответе, чтобы иметь этот именованный шаблон, он должен был быть таким при первом редактировании, но почему-то я забыл об этом изменении и просто поместил туда комментарий. - person Martin Honnen; 25.06.2014
comment
Получил этого Мартина! Большое спасибо за твою помощь ! Я создам полный XSL, используя предоставленные вами рекомендации, и опубликую его здесь снова для вашего обзора (если вы не возражаете :-)). Я все еще жду свою 30-дневную пробную лицензию Saxon 9.5 EE. Оно уже должно было прийти, но почему-то задержалось. - person user3769767; 25.06.2014
comment
Привет, Мартин, я создал XSL в соответствии с вашими рекомендациями и разместил новый XSL в своем вопросе выше. Тег ‹apply-templates› не работал для процессора Saxonica 9.5 EE XSLT. Я получил ошибку: java.lang.ClassCastException: net.sf.saxon.expr.instruct.Template не может быть приведен к com.saxonica.stream.TemplateInversion и обнаружил, что это ошибка в Saxonica, как описано в ссылке: saxonica.plan.io/issues/1632 - person user3769767; 25.06.2014
comment
Поэтому вместо этого я использовал цикл for-each ‹xsl:for-each select=copy-of(ns0:ORM_O01/ns0:ORM_O01.PATIENT/ns0:PID)› под тегом ‹xsl:stream›, как показано в моем XSL в вопросе. Это правильный подход или есть другая альтернатива ‹apply-templates›... Заранее спасибо! - person user3769767; 25.06.2014
comment
Насколько я понимаю, этот отчет об ошибке относится только к Saxon 9.4, но я не могу сказать наверняка. Боюсь, я не могу проверить себя, я думаю, вам нужно либо подождать, пока Майкл Кей ответит здесь, либо вы можете опубликовать сообщение в списке рассылки Saxon или на форуме Saxon, там вы должны получить окончательный ответ. Если Saxon не выдает ошибку в коде, который у вас есть сейчас, я бы запустил с параметром -t, чтобы увидеть, действительно ли он не построил дерево, а обработал в потоковом режиме, тогда вы можете быть уверены, что у вас есть что-то, что работает с текущим Саксонский релиз. - person Martin Honnen; 25.06.2014
comment
Привет, Мартин. Спасибо за предложение. Я выполнил преобразование с помощью команды -t и получил следующий результат: Время компиляции таблицы стилей: 356 миллисекунд. Файл обработки: SampleInput3_0.xml. .xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser Построение дерева для файла:SampleInput3_0.xml с использованием класса net.sf.saxon.tree.tiny.TinyBuilder Дерево построено за 19 миллисекунд Размер дерева: 1218 узлов, 994 символа, 266 атрибутов Время выполнения: 81 мс Используемая память: 12345960 Содержимое пула имен: 318 записей в 236 цепочках. 9 URI - person user3769767; 26.06.2014
comment
Означает ли это, что потоковое вещание не работает? Как он создал дерево? - person user3769767; 26.06.2014