XSLT Streaming сложные документы - PullRequest
0 голосов
/ 19 января 2020

Большинство примеров потоковой передачи XSLT 3.0, которые я вижу, довольно просты и принимают данные вида

<rootTag>
 <repeatingThing>
   <CDataTag>text</CDataTag> 
   <CDataTag2>text</CDataTag2>
 </repeatingThing>
 <repeatingThing>...</repeatingThing>
</rootTag>

Предположим, вам нужно коснуться всех тегов внутри repeatingThing. В этом случае потоковая передача работает достаточно хорошо, сделайте copy-of внутри вашего repeatingThing шаблона, и вы уменьшите объем памяти до 1 / X (где X - количество повторяющихся теговThing) его оригинала.

Однако я имею дело с XML, который сильно вложен. Кроме того, из-за характера моей таблицы стилей (JSON <-> XML преобразование) мне нужно коснуться всех тегов в исходном документе XML. Подход copy-of здесь не сработает, так как содержимое распределено по многим дочерним узлам, и я бы скопировал весь XML в память, просто более явно.

Я нахожусь на потеря того, как использовать потоковую передачу для работы в этом случае. Ниже приведен скелет такого «иерархического» документа:

<n1:ElectionReport xmlns:n1="NIST_V2_election_results_reporting.xsd">
    <n1:Election>
        <n1:BallotCounts>
            <n1:DeviceClass>
                <n1:Manufacturer/>
                <n1:Model/>
                <n1:Type/>
                <n1:OtherType/>
            </n1:DeviceClass>
            <n1:GpUnitId/>
            <n1:IsSuppressedForPrivacy/>
            <n1:Round/>
            <n1:Type/>
            <n1:OtherType/>
            <n1:BallotsCast/>
            <n1:BallotsOutstanding/>
            <n1:BallotsRejected/>
        </n1:BallotCounts>
        <n1:BallotStyle>
            <n1:ExternalIdentifier>
                <n1:Type/>
                <n1:OtherType/>
                <n1:Value/>
            </n1:ExternalIdentifier>
            <n1:GpUnitIds/>
            <n1:ImageUri/>
            <n1:OrderedContent xsi:type="n1:OrderedContest">
                <n1:ContestId/>
                <n1:OrderedContestSelectionIds/>
            </n1:OrderedContent>
            <n1:PartyIds/>
        </n1:BallotStyle>
        <n1:Candidate ObjectId="">
            <n1:BallotName>
                <n1:Text Language=""/>
            </n1:BallotName>
            <n1:CampaignSlogan>
                <n1:Text Language=""/>
            </n1:CampaignSlogan>
            <n1:ContactInformation>
                <n1:AddressLine/>
                <n1:Directions>
                    <n1:Text Language=""/>
                </n1:Directions>
                <n1:Email/>
                <n1:Fax/>
                <n1:LatLng>
                    <n1:Latitude/>
                    <n1:Longitude/>
                    <n1:Source/>
                </n1:LatLng>
                <n1:Name/>
                <n1:Phone/>
                <n1:Schedule>
                    <n1:Hours>
                        <n1:Day/>
                        <n1:StartTime/>
                        <n1:EndTime/>
                    </n1:Hours>
                    <n1:IsOnlyByAppointment/>
                    <n1:IsOrByAppointment/>
                    <n1:IsSubjectToChange/>
                    <n1:StartDate/>
                    <n1:EndDate/>
                </n1:Schedule>
                <n1:Uri/>
            </n1:ContactInformation>
            <n1:ExternalIdentifier>
                <n1:Type/>
                <n1:OtherType/>
                <n1:Value/>
            </n1:ExternalIdentifier>
            <n1:FileDate/>
            <n1:IsIncumbent/>
            <n1:IsTopTicket/>
            <n1:PartyId/>
            <n1:PersonId/>
            <n1:PostElectionStatus/>
            <n1:PreElectionStatus/>
        </n1:Candidate>
    </n1:Election>
    <n1:SequenceStart/>
    <n1:SequenceEnd/>
    <n1:Status/>
    <n1:TestType/>
    <n1:VendorApplicationId/>
</n1:ElectionReport>

Использование Saxon-EE 9.8.0.12

1 Ответ

1 голос
/ 19 января 2020

Этот пример, на который вы ссылаетесь, слишком длинный, чтобы я мог судить об этом, но по крайней мере некоторые шаблоны написаны в стиле, который кажется слишком многословным, даже если вы не хотите использовать потоковую передачу, например

<xsl:template name="cdf:LatLng" match="element(*, cdf:LatLng)">
    <xsl:param name="set_type" select="false()"/>
    <xsl:where-populated>
        <string key="Label">
            <xsl:value-of select="@Label"/>
        </string>
    </xsl:where-populated>
    <xsl:where-populated>
        <number key="Latitude">
            <xsl:value-of select="cdf:Latitude"/>
        </number>
    </xsl:where-populated>
    <xsl:where-populated>
        <number key="Longitude">
            <xsl:value-of select="cdf:Longitude"/>
        </number>
    </xsl:where-populated>
    <xsl:where-populated>
        <string key="Source">
            <xsl:value-of select="cdf:Source"/>
        </string>
    </xsl:where-populated>
    <xsl:if test="not($set_type)">
        <string key="@type">ElectionResults.LatLng</string>
    </xsl:if>
</xsl:template>

кажется выполнимым как

<xsl:template match="LatLng">
    <xsl:param name="set_type" select="false()"/>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates/>
    <xsl:if test="not($set_type)">
        <string key="@type">ElectionResults.LatLng</string>
    </xsl:if>
</xsl:template>

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

<xsl:template match="element(*, xs:string)">
    <string key="{local-name()}">{.}</string>
</xsl:template>

<xsl:template match="element(*, xs:double) | element(*, xs:decimal)">
    <number key="{local-name()}">{.}</number>
</xsl:template>

Конечно, это в основном предполагает, что дочерние элементы должны обрабатываться в том порядке, в котором они присутствуют, и вы хотите, чтобы все они обрабатывались, но последнее ограничение можно ослабить даже при потоковой передаче, если вы используете, например, <xsl:apply-templates select="*[self::foo or self::bar]"/>.

По крайней мере, если вы просто хотите отобразить ваши известные типы схем на JSON и прописать множество различных шаблонов для различных элементов, я думаю, что использование apply-templates вместо указания различных дочерних выборов может помочь сделать код растекаемый. Для типов, где у вас есть возможные minOccurs = 0 и maxOccurs = unbounded, я думаю, что вы можете жить с

<xsl:for-each-group select="*" group-by="node-name()">
  <xsl:variable name="sibling-group" select="copy-of(current-group())"/>
  <xsl:choose>
     <xsl:when test="tail($sibling-group)">
        <array key="{local-name()}">
           <xsl:apply-templates select="$sibling-group"/>
        </array>
     </xsl:when>
     <xsl:otherwise>
        <xsl:apply-templates select="$sibling-group"/>
     </xsl:otherwise>
  </xsl:choose>
</xsl:for-each-group>

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

Если вы хотите сохранить подробный стиль с явным выбором различных дочерних элементов в одном и том же шаблоне, вы можете попробовать, насколько хорошо Saxon справится с использованием xsl:fork, например,

<xsl:template name="cdf:LatLng" match="element(*, cdf:LatLng)">
    <xsl:param name="set_type" select="false()"/>
    <xsl:fork>
     <xsl:sequence>
      <xsl:where-populated>
        <string key="Label">
            <xsl:value-of select="@Label"/>
        </string>
      </xsl:where-populated>
     </xsl:sequence>
     <xsl:sequence>
      <xsl:where-populated>
        <number key="Latitude">
            <xsl:value-of select="cdf:Latitude"/>
        </number>
      </xsl:where-populated>
     </xsl:sequence>
     <xsl:sequence>
      <xsl:where-populated>
        <number key="Longitude">
            <xsl:value-of select="cdf:Longitude"/>
        </number>
      </xsl:where-populated>
     </xsl:sequence>
     <xsl:sequence>
      <xsl:where-populated>
        <string key="Source">
            <xsl:value-of select="cdf:Source"/>
        </string>
      </xsl:where-populated>
     </xsl:sequence>
    </xsl:fork>
    <xsl:if test="not($set_type)">
        <string key="@type">ElectionResults.LatLng</string>
    </xsl:if>
</xsl:template>

Использование call-template, которое у вас также есть, будет невозможно при потоковой передаче вообще. Кажется, он также используется в этой таблице стилей для обработки XML элементов в другом порядке, нежели порядок ввода, он, похоже, выводит любые подэлементы, объявленные в абстрактных типах, после тех, которые объявлены в расширенных типах. Это, конечно, не очень хорошо работает с потоковым подходом только перенаправления, узел за узлом обработки. Так что, думаю, вам нужно решить, не можете ли вы сначала вывести базовые подэлементы в JSON.

...