Как вставить текст с помощью XSLT v1.0 вместо использования регулярного выражения XSLT v2.0? - PullRequest
4 голосов
/ 05 октября 2010

У меня есть XML-файл, который описывает (среди прочего) элементы со значениями атрибутов, которые описывают полностью определенные имена классов Java. Я пытаюсь написать XSLT-преобразование, чтобы изменить имена классов, описанные в этом файле, чтобы (например) вхождения com.example.MyClass стали com.example.MockMyClass.

Вот снова этот пример в контексте фрагмента исходного файла:

<event type="node-enter">
  <action name="MyActionName" class="com.example.MyClass">
    <bodyTemplate>
      templates/MyTemplate.vm
    </bodyTemplate>
  </action>
</event>

Я хочу, чтобы результат был:

<event type="node-enter">
  <action name="MyActionName" class="com.example.MockMyClass">
    <bodyTemplate>
      templates/MyTemplate.vm
    </bodyTemplate>
  </action>
</event>

Я выполняю это преобразование, используя Java JAXP API, и написал прекрасную подпрограмму, поддерживающую XSLT 2.0, для получения желаемых результатов, но обнаружил, что Java 5 не поддерживает XSLT 2.0, который требуется для регулярных выражений поддержка.

Итак, мой вопрос: как лучше всего добиться этого, используя архаичный API JAXP XSLT 1.0? То есть без использования регулярных выражений. Я искал похожие проблемы, но требование к группам регулярных выражений с обратными ссылками, кажется, делает это сложным. Этот вопрос является началом, но мне нужно вставить текст в соответствующую строку, а не просто , заменив .

Для справки, вот моя попытка регулярного выражения (XSLT 2.0):

<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
  <xsl:template match='/'>
    <xsl:analyze-string select='action/@class' regex='([A-Za-z0-9]+[$\.])+([A-Za-z0-9]+)'>
      <xsl:matching-substring>
        <xsl:value-of select='regex-group(1)'/>
        <xsl:text>Mock</xsl:text>
        <xsl:value-of select='regex-group(2)'/>
      </xsl:matching-substring>
      <xsl:non-matching-substring>
        <xsl:value-of select='.'/>
      </xsl:non-matching-substring>
    </xsl:analyze-string>
  </xsl:template>
</xsl:stylesheet>

Ответы [ 2 ]

3 голосов
/ 05 октября 2010

Как насчет следующего?

<xsl:template name="classname">
    <xsl:param name="class"/>
    <xsl:choose>
        <xsl:when test="contains($class,'.')">
            <xsl:value-of select="concat(substring-before($class,'.'),'.')"/>
            <xsl:call-template name="classname">
                <xsl:with-param name="class"
                                    select="substring-after($class,'.')"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="concat('Mock',$class)"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Принимает имя класса в качестве входного параметра и добавляет "Mock" после последнего ".".Вы можете вызвать его, например, с помощью

 <xsl:call-template name="classname">
     <xsl:with-param name="class" select="@class"/>
 </xsl:call-template>

(я только что быстро попробовал в Firefox, возможно, вам понадобится немного убрать пробелы).

2 голосов
/ 05 октября 2010

Следующий код выглядит длинным, однако он использует готовые детали (шаблон strRev предоставляется FXSL и не требует переписывания). Кроме того, почти половина кода является шаблоном идентификации и передачей параметров в <xsl:call-template>. Это очень коротко в XSLT 2.0.

Когда у нас есть готовые меньшие части / функции, такие как strRev template / reverse(), тогда это решение не требует написания длинного и подверженного ошибкам домашнего рекурсивного кода.

Основная идея состоит в том, что последний '.' символ в строке является первым '.' символом в обратной строке .

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

 <xsl:param name="pPrepend" select="'Mock'"/>
 <xsl:variable name="vRevPrepend">
  <xsl:call-template name="strRev">
   <xsl:with-param name="pText" select="$pPrepend"/>
  </xsl:call-template>
 </xsl:variable>


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

 <xsl:template match="action/@class">
   <xsl:variable name="vRevText">
    <xsl:call-template name="strRev"/>
   </xsl:variable>

   <xsl:variable name="vRevNew" select=
   "concat(substring-before($vRevText,'.'), $vRevPrepend,
           '.', substring-after($vRevText,'.'))"/>

   <xsl:variable name="vNewText">
     <xsl:call-template name="strRev">
      <xsl:with-param name="pText" select="$vRevNew"/>
     </xsl:call-template>
   </xsl:variable>

  <xsl:attribute name="class">
   <xsl:value-of select="$vNewText"/>
  </xsl:attribute>
 </xsl:template>

 <xsl:template name="strRev">
  <xsl:param name="pText" select="."/>

  <xsl:if test="string-length($pText)">
   <xsl:call-template name="strRev">
    <xsl:with-param name="pText" select="substring($pText,2)"/>
   </xsl:call-template>
   <xsl:value-of select="substring($pText,1,1)"/>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

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

<event type="node-enter">
  <action name="MyActionName" class="com.example.MyClass">
    <bodyTemplate>
      templates/MyTemplate.vm
    </bodyTemplate>
  </action>
</event>

желаемый, правильный результат выдается :

<event type="node-enter">
    <action name="MyActionName" class="com.example.MockMyClass">
        <bodyTemplate>
          templates/MyTemplate.vm
        </bodyTemplate>
    </action>
</event>

II. Решение XSLT 2.0 :

Точно такой же алгоритм, но в XSLT 2.0 действительно короткий :

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes"/>

 <xsl:param name="pPrepend" select="'Mock'"/>

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

    <xsl:template match="action/@class">
     <xsl:attribute name="class" select=
     "my:strRev(concat(substring-before(my:strRev(.),'.'),
                       my:strRev($pPrepend),'.',
                       substring-after(my:strRev(.),'.')
                       )
                )
     "/>
    </xsl:template>

    <xsl:function name="my:strRev" as="xs:string">
      <xsl:param name="pText" as="xs:string"/>

      <xsl:sequence select=
       "codepoints-to-string(reverse(string-to-codepoints($pText)))
       "/>
    </xsl:function>
</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...