2013-09-05 4 views
3

Следующий фрагмент XSLT используется для переноса/маркировки первого и последнего текстовых узлов с заданным атрибутом @audience для выделения.XSLT Match Node Not Value

<xsl:template match="text()"> 
    <xsl:if test=". = ((((ancestor::*[contains(@audience,'FLAG_')])[last()])/descendant::text())[1])">     
     <xsl:call-template name="flagText"/>    
    </xsl:if> 
    <xsl-value-of select="."/> 
    <xsl:if test=". = ((((ancestor::*[contains(@audience,'FLAG_')])[last()])/descendant::text())[last()])">     
     <xsl:call-template name="flagText"/>    
    </xsl:if> 
</xsl:template> 

Псевдо-код:
Найти последний (ближайший) предка элемент, который соответствует критериям флага, а затем найти первый и последний текстовые узлы, которые являются потомками этого элемента и флаг им.

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

Пример:
The quick brown fox jumped over the lazy dog.

Токовый выход:
[FLAG]The quick brown fox jumped over [FLAG]the lazy dog[FLAG].

код [1] и собаки [последняя()] надлежащим образом помечено, но это также ловя слово 'и' в из-за соответствия строки или ее первого.

Edit:
Ожидаемое (желательно) выход:
[FLAG]The quick brown fox jumped over the lazy dog.[FLAG]

Как реорганизовать свое заявление только соответствовать первый и последний узлы? Я не хочу сравнивать строки, которые я просто хочу выбрать первый и последний.

Edit:

Пример Источник XML

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd"> 
<concept audience="Users" id="concept_lsy_5vg_kl"><title>Product ABC</title><conbody><p>This is a blurb about <ph>Product ABC</ph>. Since the text in the phrase (ph) matches the text node in the title (first text node) it will be flagged. I only want the first and last nodes flagged. Basically, I don't want to compare the contents of the nodes. <ph audience="Users">I also need to support inline cases such as this one. </ph>I just want the flags before and after the first and last text nodes for each audience match.</p></conbody></concept> 

Пример XSLT

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

    <xsl:output method="text" omit-xml-declaration="yes" indent="yes"/> 

    <xsl:template match="text()"> 
     <xsl:if test=". = ((((ancestor::*[contains(@audience,'Users')])[last()])/descendant::text())[1])">   
      <xsl:text>[USERS]</xsl:text>    
     </xsl:if> 
     <xsl:value-of select="."/> 
     <xsl:if test=". = ((((ancestor::*[contains(@audience,'Users')])[last()])/descendant::text())[last()])">   
      <xsl:text>[/USERS]</xsl:text>   
     </xsl:if> 
    </xsl:template> 



</xsl:stylesheet> 

Выходной ток

[ПОЛЬЗОВАТЕЛИ] Продукт ABCThis - это рекламный ролик о продукте [USERS] ABC. Поскольку текст фразы (ph) соответствует текстовому узлу в заголовке (первый текстовый узел), он будет помечен. Я хочу, чтобы только первый и последний узлы помечены. В принципе, я не хочу сравнивать содержимое узлов. [ПОЛЬЗОВАТЕЛИ] Мне также необходимо поддерживать встроенные случаи, такие как этот. [/ USERS] Я просто хочу флаги до и после первых и последних текстовых узлов для каждого матча аудитории. [/ USERS]

Желаемых Выходных [USERS] ABCThis Продукта представляет собой рекламное объявление о продукте ABC. Поскольку текст фразы (ph) соответствует текстовому узлу в заголовке (первый текстовый узел), он будет помечен. Я хочу, чтобы только первый и последний узлы помечены. В принципе, я не хочу сравнивать содержимое узлов. [ПОЛЬЗОВАТЕЛИ] Мне также необходимо поддерживать встроенные случаи, такие как этот. [/ USERS] Мне просто нужны флаги до и после первого и последнего текстовых узлов для каждой аудитории. [/ USERS]

Спасибо.

+0

Будет ли это возможно для Вас, чтобы показать копию входного XML (в идеале тот, который соответствует вам текущий выход). Кроме того, вы могли бы показать свой ожидаемый результат тоже, просто чтобы понять это. Благодаря! –

+0

Источник - это DITAMAP, который становится 20-страничным PDF-файлом. Мы в основном выделяем или помещаем текст для рецензентов, чтобы они могли проверить наши условия (они не читают XML). Я попытаюсь создать рабочий образец для обзора. Благодарю. –

+0

Сделано! Надеюсь, что этот пример полезен. Я ценю любые советы. –

ответ

1

В вашем выражении, вы получаете «последний» предка с именем класса «Пользователи» для текстового узла, вы на

...(ancestor::*[contains(@audience,'FLAG_')])[last()])... 

Но вы действительно хотите, чтобы получить первый здесь. То есть, самый непосредственный предок. Список предков будет начинаться с родительского узла, то гранд-родителя, и т.д ...

...(ancestor::*[contains(@audience,'FLAG_')])[1])... 

Кроме того, вы говорите, что вы не хотите, чтобы сравнить строки, просто выяснить, если текст узла первый или последний, но ваше текущее сравнение проверяет строки. Итак, если у вас есть два текстовых узла с одним и тем же текстом (один из которых является первым), тогда вы получите тэг «[USER]». Чтобы проверить, являются ли два узла одним и тем же узлом (в отличие от одного и того же содержимого), вы можете использовать функцию generate-id.

generate-id() = generate-id(((ancestor::*[contains(@audience,'Users')][1])/descendant::text())[1]) 

Попробуйте XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" > 
    <xsl:output method="text" omit-xml-declaration="yes" indent="yes"/> 

    <xsl:template match="text()"> 
    <xsl:if test="generate-id() = generate-id(((ancestor::*[contains(@audience,'Users')][1])/descendant::text())[1])"> 
     <xsl:text>[USERS]</xsl:text> 
    </xsl:if> 
    <xsl:value-of select="."/> 
    <xsl:if test="generate-id() = generate-id(((ancestor::*[contains(@audience,'Users')][1])/descendant::text())[last()])"> 
     <xsl:text>[/USERS]</xsl:text> 
    </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

НО .... Может быть, вы можете использовать другой подход. Глядя исключительно на ваш XML и ожидаемый результат, можно использовать другой подход, который должен иметь шаблон, соответствующий любому элементу с классом «users», и просто выписывать тег start и end [USER] в том, что с xsl : apply-templates между ними выбрать весь текст.

Попробуйте XSLT в качестве альтернативы:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" > 
    <xsl:output method="text" omit-xml-declaration="yes" indent="yes"/> 

    <xsl:template match="*[contains(@audience,'Users')]"> 
    <xsl:text>[USERS]</xsl:text> 
     <xsl:apply-templates /> 
    <xsl:text>[/USERS]</xsl:text> 
    </xsl:template> 
</xsl:stylesheet> 
+0

У меня не было возможности попробовать альтернативное решение, но вы находитесь в своем главном ответе. Огромное спасибо. Большая часть моей работы была основана на ложном предположении, что ancestor :: * [last()] был ближайшим элементом, который соответствовал моему фильтру. –

1

Извините за основной вопрос, но я не понимаю, почему вы просто не используете файл ditaval, чтобы выделить текст, когда @audience настроен на пользователей? Вы можете просто в основном покрасить текст, чтобы ваши рецензенты могли найти текст и проверить его. После обзора вы можете просто отключить выделение.

Возможно, я не понимаю ваши намерения.

+0

DITAVAL с PDF прослушивается DITA OT. См. Https://github.com/dita-ot/dita-ot/issues/1396 и https://github.com/dita-ot/dita-ot/issues/1175 Это отмечено как ответ ... Мне все же хотелось бы помочь с оригинальным вопросом, поскольку я пишу много настраиваемых XSLT-файлов. Спасибо. –