2012-06-25 2 views
3

Я новичок в XSLT и теперь у меня есть довольно сложная проблема ... Мой входной файл выглядитНаилучшая практика для вставки и удаления элементов с помощью XSLT

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<doc> 
    <outerElement> 
    <first> 
     <textElement>Some Text</textElement> 
    </first> 
    <second> 
     <textElement>Some Text</textElement> 
    </second> 
    <third> 
     <textElement>Some Text</textElement> 
    </third> 
    </outerElement> 
</doc> 

Проблема возникает с «вторым» элементом. Во входном файле он может иметь одну из трех форм:

MISSING 

<second> 
    <textElement>Some Text</textElement> 
</second> 

<second missingCause="" /> 

В выходном файле он должен быть вставлен как вторая форма. Если он отсутствовал до того, как textElement должен указать, что он был вставлен шаблоном, и что важно здесь, он должен быть на второй позиции, потому что я хочу проверить его на схеме xsd ...

Вот мой текущий XSL :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <!-- COPY ALL ELEMENTS --> 
    <xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
    </xsl:template> 

    <!-- remove Elements with attribute deleteme --> 
    <xsl:template match="outerElement/second[@missingCause='*']" /> 

    <!-- look if second is there. If not insert --> 
    <xsl:template match="outerElement"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
     <xsl:if test="not(second)"> 
     <second> 
     </second> 
     </xsl:if> 
    </xsl:copy>  
    </xsl:template> 

    <!-- Insert Element second --> 
    <xsl:template match="outerElement/second"> 
    <xsl:apply-templates select="node()|@*"/> 
    <xsl:copy> 
     <xsl:if test="not(textElement)"> 
     <textElement>Inserted by Template</textElement> 
     </xsl:if> 
    </xsl:copy>  
    </xsl:template> 

</xsl:stylesheet> 

Если «второй» отсутствует мой выходной файл просто получает элемент «», но его пустым и последнее преобразование не будет применена. Все остальное выглядит хорошо, когда у меня в Документе я получаю предупреждение ...

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

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

Спасибо за чтение;)

ответ

4

Вот общее решение, которое работает для любого количества по-разному названных детей outerElement и любой предпочитаемой порядка между ними:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:param name="pUncertainElName" select="'second'"/> 

<xsl:param name="pOrderedNames" select="'|first|second|third|'"/> 

<xsl:template match="node()|@*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="outerElement"> 
    <xsl:variable name="vrtfFirstPass"> 
     <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
     <xsl:apply-templates select= 
     "self::*[not(*[name() = $pUncertainElName]) 
       or 
       *[name()=$pUncertainElName and @missing-cause]]" 
     mode="missing"/> 
     </xsl:copy> 
    </xsl:variable> 

    <xsl:apply-templates select="ext:node-set($vrtfFirstPass)/*" mode="pass2"/> 
</xsl:template> 

<xsl:template match="*[@missing-cause]"/> 

<xsl:template match="*" mode="missing"> 
    <xsl:element name="{$pUncertainElName}"> 
     <textElement>Some Text</textElement> 
    </xsl:element> 
</xsl:template> 

<xsl:template match="outerElement" mode="pass2"> 
    <xsl:copy> 
    <xsl:apply-templates> 
    <xsl:sort data-type="number" select= 
    "string-length(substring-before($pOrderedNames, 
            concat('|', name(), '|') 
            ) 
        )"/> 
    </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

Когда это преобразование применяется на следующий XML-документ:

<doc> 
    <outerElement> 
     <first> 
      <textElement>Some Text</textElement> 
     </first> 
     <second missing-cause=""> 
      <textElement>Some Text</textElement> 
     </second> 
     <third> 
      <textElement>Some Text</textElement> 
     </third> 
    </outerElement> 
</doc> 

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

<doc> 
    <outerElement> 
     <first> 
     <textElement>Some Text</textElement> 
     </first> 
     <second> 
     <textElement>Some Text</textElement> 
     </second> 
     <third> 
     <textElement>Some Text</textElement> 
     </third> 
    </outerElement> 
</doc> 

Когда он применяется в этом XML-документ:

<doc> 
    <outerElement> 
     <first> 
      <textElement>Some Text</textElement> 
     </first> 
     <third> 
      <textElement>Some Text</textElement> 
     </third> 
    </outerElement> 
</doc> 

снова разыскиваемый, правильный результат получается:

<doc> 
    <outerElement> 
     <first> 
     <textElement>Some Text</textElement> 
     </first> 
     <second> 
     <textElement>Some Text</textElement> 
     </second> 
     <third> 
     <textElement>Some Text</textElement> 
     </third> 
    </outerElement> 
</doc> 

Fin союзник, когда же преобразование применяется на этот XML-документ:

<doc> 
    <outerElement> 
     <first> 
      <textElement>Some Text</textElement> 
     </first> 
     <second> 
      <textElement>Some Text</textElement> 
     </second> 
     <third> 
      <textElement>Some Text</textElement> 
     </third> 
    </outerElement> 
</doc> 

опять же хотел, правильный результат получается:

<doc> 
    <outerElement> 
     <first> 
     <textElement>Some Text</textElement> 
     </first> 
     <second> 
     <textElement>Some Text</textElement> 
     </second> 
     <third> 
     <textElement>Some Text</textElement> 
     </third> 
    </outerElement> 
</doc> 

ли Примечание:

Там может быть любое неограниченное число детей с иными именами outerElement (не только три), и их порядок может быть неизвестен заранее.

Например:

Учитывая этот XML документ:

<doc> 
    <outerElement> 
     <first> 
      <textElement>Some Text</textElement> 
     </first> 
     <second missing-cause=""> 
      <textElement>Some Text</textElement> 
     </second> 
     <third> 
      <textElement>Some Text</textElement> 
     </third> 
     <fourth> 
      <textElement>Some Text</textElement> 
     </fourth> 
    </outerElement> 
</doc> 

и этот порядок: вперед, второй, третий, первый

У нас есть только заменить:

<xsl:param name="pOrderedNames" select="'|first|second|third|'"/> 

с:

<xsl:param name="pOrderedNames" select="'|fourth|second|third|first|'"/> 

И теперь новый разыскиваемый результат получается:

<doc> 
    <outerElement> 
     <fourth> 
     <textElement>Some Text</textElement> 
     </fourth> 
     <second> 
     <textElement>Some Text</textElement> 
     </second> 
     <third> 
     <textElement>Some Text</textElement> 
     </third> 
     <first> 
     <textElement>Some Text</textElement> 
     </first> 
    </outerElement> 
</doc> 

Объяснение:

  1. Это двухпроходное преобразование.

  2. Название элемента, который может присутствовать или не может присутствовать, указывается во внешнем/глобальном параметре $pUncertainElName. Для удобства этого объяснения мы ссылаемся на этот элемент как second.

  3. На первом проходе, все дети outerElement за исключением second, который имеет атрибут missing-cause, копируются «как есть». Если элемент second отсутствовал или имел атрибут missing-cause, мы выводим новый дочерний элемент outerElement - точно необходимый элемент second.

  4. На втором проходе мы сортируем ребенок из outerElement, полученных в первом проходе, в соответствии с их приоритетом, как указаны в другом внешнем/глобальном параметре, названный $pOrderedNames (имя, которое осталось от другого имени в этой строке , имеет более высокий приоритет)

+0

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

+0

@DennisIch: Это называется * многопроходной обработкой *. Основная идея заключается в том, что вывод каждого прохода фиксируется в переменной xsl: а затем в следующем проходе шаблоны применяются к дереву, содержащемуся в этой переменной. Я предполагаю, что вас смущает использование функции расширения ext: node-set() - это необходимо в XSLT 1.0 (не требуется в XSLT 2.0) из-за ограничений (печально известного типа RTF) XSLT 1.0 , См. Например, этот ответ: http://stackoverflow.com/a/6529617/36305 –

+0

ОК, чтобы уточнить ... Этот шаблон, в котором переменная находится в самой переменной, не производит никакого вывода, а обрабатывается, и он копирует личность и тогда? Этот второй выбор запускает «отсутствующий» шаблон? Я смущен тем, как он перескакивает из шаблона в шаблон; (- Не было бы проще/яснее, если я буду хранить OuterElement в переменной и использовать xsl: call-template на нем? Тогда я мог бы создать несколько недостающих переменных. –

3

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

<xsl:template match="outerElement[not(second) or second[@missingCause='']]"> 
    <xsl:apply-templates select="@*|first"/> 
    <second> 
     <textElement>Inserted by Template</textElement> 
    </second> 
    <xsl:apply-templates select="third"/> 
</xsl:template> 
+0

Его работа как ожидается спасибо :) - почему первый выбирает как «@ * | первый» и последний select = «третий»? Есть ли способ автоматизировать, где этот элемент помещается в xml, чтобы он вписывался в схему xsd, или я должен сам все это делать? –

+0

С первым выбором мы гарантируем, что шаблоны применяются к атрибутам outerElement. Я не понимаю второй вопрос. –

+0

ah ok :) - Для второго вопроса .. У меня есть схема и в этой схеме некоторые элементы (0-1), и некоторые из этих элементов являются обязательными для моего приложения. Поэтому я получаю данные, где эти элементы отсутствуют, и я хочу, чтобы все они были с XSL. Есть ли способ рассказать XSL, что он должен заказать элементы, которые я вставляю, чтобы соответствовать схеме, чтобы она проверялась. Есть несколько Элементов с огромным количеством детей и применение шаблона один в порядке, это будет тяжелая работа, потому что я должен принимать каждый элемент, который может быть там учтен. –

Смежные вопросы