2017-02-21 8 views
0

Я ищу элегантное решение XSLT 1.0 или 2.0 для следующей задачи преобразования. Я упростил разметку ниже, так что «a» - это начальный элемент вехи, а «b» - это конечный элемент вехи. Все между «a» и «b» должно быть обернуто элементом «c».XSLT-преобразование элементов одноэлементности/вехи

Входной сигнал:

<doc> 
<line>Text text <a/>text<b/></line> 
<line>Text <a/>text text</line> 
<line>Text<b/> text <a/>text</line> 
<line>Text text text</line> 
<line>Text text<b/> text</line> 
</doc> 

Выход:

<doc> 
<line>Text text <c>text</c></line> 
<line>Text <c>text text</c></line> 
<line><c>Text</c> text <c>text</c></line> 
<line><c>Text text text</c></line> 
<line><c>Text text</c> text</line> 
</doc> 

В реальной случае, есть по крайней мере 5 вариаций каждого из а/б/с элементами для удовлетворения. Существуют также десятки тысяч строк, многие из которых не содержат a/b или их вариаций. В реальном случае также есть группа секций, разделяющих «сек», где поведение a/b по-прежнему должно работать.

Наше первоначальное решение подразумевало использование предшествующих :: *, но это, очевидно, приносит значительные проблемы с производительностью для больших XML-документов, поэтому это не приемлемое решение.

+0

Возможно, вы сможете решить проблему с производительностью, просто используя предыдущий, а не предыдущий. Если строки содержат гораздо большее количество узлов, чем показанные, предыдущие братья и последующие братья не должны быть чрезмерно дорогими. –

+0

Спасибо @MichaelKay. В настоящее время мы используем предыдущий брат, но он по-прежнему не масштабируется, потому что нам нужно оглянуться назад по всему документу, чтобы правильно определить текущее состояние строки. Я предполагаю, что нам придется переключиться на подход обработки SAX, чтобы решить эту проблему, используя простой набор флагов состояния. –

+0

ОК, я думаю, я не оценил семантику ваших веха. Это предполагает, что вам нужно двигаться вперед по линиям, поддерживая состояние либо с помощью рекурсии, либо в XSLT 3.0 с помощью xsl: iterate. –

ответ

0

Благодаря Michaely Kay для вдохновения. Забыв на мгновение о механике обертывания < с > .. </с > элементы, правильный алгоритм как это реализовано в XSLT 3.0, как представляется:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="xml" version="1.0" encoding="UTF-8" /> 

<xsl:template match="/doc" > 
    <xsl:copy> 
     <xsl:iterate select="line"> 
      <xsl:param name="state">b</xsl:param> 
      <xsl:variable name="lastState" select="local-name((a|b)[last()])"/> 
      <xsl:variable name="nextState"> 
       <xsl:choose> 
        <xsl:when test="$lastState"> 
         <xsl:value-of select="$lastState"/> 
        </xsl:when> 
        <xsl:otherwise> 
         <xsl:value-of select="$state"/> 
        </xsl:otherwise> 
       </xsl:choose> 
      </xsl:variable> 

      <xsl:copy> 
       <xsl:if test="$state = 'a'"><startC/></xsl:if> 
       <xsl:apply-templates/> 
       <xsl:if test="$nextState = 'a'"><endC/></xsl:if>      
      </xsl:copy> 

      <xsl:next-iteration> 
       <xsl:with-param name="state" select="$nextState"/> 
      </xsl:next-iteration>           
     </xsl:iterate> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="a"> 
    <startC/> 
</xsl:template> 

<xsl:template match="b"> 
    <endC/> 
</xsl:template> 

Предоставление выход:

<doc> 
    <line>Text text <startC/>text<endC/></line> 
    <line>Text <startC/>text text<endC/></line> 
    <line><startC/>Text<endC/> text <startC/>text<endC/></line> 
    <line><startC/>Text text text<endC/></line> 
    <line><startC/>Text text<endC/> text</line> 
</doc> 
Смежные вопросы