Как включить xml в xsl: комментировать и поддерживать отступы - PullRequest
2 голосов
/ 05 июня 2019

Я пишу некоторые xsl для преобразования схемы книги в xml в набор отдельных файлов xml (по одному на каждую главу, а также немного frontmatter и т. Д.). Я сделаю это с <exsl:document>, и большая часть содержимого отдельных файлов будет написана внутри xsl.

Я использую xsltproc, поэтому xslt 1.0.

Я хочу текстовые комментарии, которые я могу получить, используя <xsl:comment>, но также и некоторый xml, который "закомментирован". Как упоминалось в этом вопросе , это невозможно при использовании <xsl:comment>.

В ответе *1013* на этот вопрос используется <xsl:text disable-output-escaping="yes">&lt;!--</xsl:text>, чтобы обернуть прокомментированный xml. Это работает, за исключением того, что, как только вы добавите его, выходные данные больше не будут иметь правильный отступ.

Например, следующий xsl:

<?xml version='1.0' encoding="UTF-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:template match="/">
<root>
  <xsl:comment>Text comment</xsl:comment>
  <child><name>A child</name></child>
  <xsl:text disable-output-escaping="yes">&lt;!--</xsl:text>
  <child><name>commented child</name></child>
  <xsl:text disable-output-escaping="yes">--&gt;</xsl:text>
</root>
</xsl:template>

</xsl:stylesheet>

дает xml внутри комментария, но без отступа:

<root><!--Text comment--><child><name>A child</name></child><!--<child><name>commented child</name></child>--></root>

при использовании этого:

<xsl:template match="/">
<root>
  <xsl:comment>Text comment</xsl:comment>
  <child><name>A child</name></child>
  <xsl:comment><child><name>commented child</name></child></xsl:comment>
</root>
</xsl:template>

дает хороший отступ, но в комментарии нет xml-тегов:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <!--Text comment-->
  <child>
    <name>A child</name>
  </child>
  <!--commented child-->
</root>

Есть ли способ сохранить отступ, но поместить код xml в комментарии?

1 Ответ

0 голосов
/ 23 июня 2019

Вот преобразование, которое можно использовать для закомментирования любого узла или фрагмента XML и которое :

  1. Не не использует DOE
  2. Сохраняет отступ
  3. Даже пытается выразить вложенные комментарии - не игнорирует комментарии и не выдает ошибку, когда встречается комментарий для комментариев:

=============================

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text" indent="yes"/>

  <xsl:template match="/">
    <xsl:value-of select="'&lt;!--&#xA;'"/>
      <xsl:apply-templates select="*" mode="escapeToText"/>
    <xsl:value-of select="'&#xA;-->'"/>
  </xsl:template>


  <xsl:template match="*" mode="escapeToText">
    <xsl:value-of select="concat('&lt;', name())"/>

    <xsl:apply-templates select="@*" mode="escapeToText"/>
    <xsl:value-of select="'>'"/>

    <xsl:apply-templates select="node()" mode="escapeToText"/>
    <xsl:value-of select="concat('&lt;/', name(), '>')"/>
  </xsl:template>

  <xsl:template match="@*" mode="escapeToText">
    <xsl:value-of select="concat(' ', name(), '=&quot;')"/>
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="string(.)"/>
    </xsl:call-template>
    <xsl:value-of select="'&quot;'"/>
  </xsl:template>

  <xsl:template match="text()" mode="escapeToText">
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="."/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="processing-instruction()" mode="escapeToText">
    <xsl:value-of select="concat('&lt;?', name(), ' ', ., ' ?>')"/>
  </xsl:template>

  <xsl:template match="comment()">
    <xsl:value-of select="concat('&lt;!CM ', ., 'CM>')"/>
  </xsl:template>

  <xsl:template name="escapeDoubleDash">
   <xsl:param name="pText"/>

   <xsl:choose>
     <xsl:when test="contains($pText, '--')">
       <xsl:value-of select="substring-before($pText, '--')"/>
       <xsl:value-of select="'!-!-'"/>
       <xsl:call-template name="escapeDoubleDash">
         <xsl:with-param name="pText" select="substring-after($pText, '--')"/>
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise><xsl:value-of select="$pText"/></xsl:otherwise>
   </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

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

<input>
    <name>Jack</name>
    <age>23</age>
    <type-10-num>1</type-10-num>
    <type-20-num>2</type-20-num>
    <type-20-char>3</type-20-char>
    <type-180-num>4</type-180-num>
    <type-180-char>5</type-180-char>
    <type-180-str>6</type-180-str>
</input>

желаемый результат с отступом получается :

<!--
<input>
    <name>Jack</name>
    <age>23</age>
    <type-10-num>1</type-10-num>
    <type-20-num>2</type-20-num>
    <type-20-char>3</type-20-char>
    <type-180-num>4</type-180-num>
    <type-180-char>5</type-180-char>
    <type-180-str>6</type-180-str>
</input>
-->

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

Для полноты я добавил комментарий и инструкцию по обработке перед тем, как шаблон соответствует атрибутам:

  <?somePI x="y" z="t" pqr ?>

  <!-- A Comment node -->

И результат, как и ожидалось:

<!--
<xsl:stylesheet version="1.0">
 <xsl:output method="text" indent="yes"></xsl:output>

  <xsl:template match="/">
    <xsl:value-of select="'<!!-!-
'"></xsl:value-of>
      <xsl:apply-templates select="*" mode="escapeToText"></xsl:apply-templates>
    <xsl:value-of select="'
!-!->'"></xsl:value-of>
  </xsl:template>


  <xsl:template match="*" mode="escapeToText">
    <xsl:value-of select="concat('<', name())"></xsl:value-of>

    <xsl:apply-templates select="@*" mode="escapeToText"></xsl:apply-templates>
    <xsl:value-of select="'>'"></xsl:value-of>

    <xsl:apply-templates select="node()" mode="escapeToText"></xsl:apply-templates>
    <xsl:value-of select="concat('</', name(), '>')"></xsl:value-of>
  </xsl:template>

  <?somePI x="y" z="t" pqr  ?>

  <!CM  A Comment node CM>
  <xsl:template match="@*" mode="escapeToText">
    <xsl:value-of select="concat(' ', name(), '="')"></xsl:value-of>
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="string(.)"></xsl:with-param>
    </xsl:call-template>
    <xsl:value-of select="'"'"></xsl:value-of>
  </xsl:template>

  <xsl:template match="text()" mode="escapeToText">
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="."></xsl:with-param>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="processing-instruction()" mode="escapeToText">
    <xsl:value-of select="concat('<?', name(), ' ', ., ' ?>')"></xsl:value-of>
  </xsl:template>

  <xsl:template match="comment()">
    <xsl:value-of select="concat('<!CM ', ., 'CM>')"></xsl:value-of>
  </xsl:template>

  <xsl:template name="escapeDoubleDash">
   <xsl:param name="pText"></xsl:param>

   <xsl:choose>
     <xsl:when test="contains($pText, '!-!-')">
       <xsl:value-of select="substring-before($pText, '!-!-')"></xsl:value-of>
       <xsl:value-of select="'!-!-'"></xsl:value-of>
       <xsl:call-template name="escapeDoubleDash">
         <xsl:with-param name="pText" select="substring-after($pText, '!-!-')"></xsl:with-param>
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise><xsl:value-of select="$pText"></xsl:value-of></xsl:otherwise>
   </xsl:choose>
  </xsl:template>
</xsl:stylesheet>
-->

Обновление :

В случае, если вы хотите, чтобы закомментированный результат появился как часть oДля XML-документа, то есть не иметь <xsl:output method="text"/>, я предлагаю добавить специальный элемент, скажем, <MyComment>, и получить результат комментирования как дочерний текстовый узел <MyComment>.Как правило, закомментированный фрагмент XML будет отображаться с экранированными специальными символами.

Но есть одна хитрость - используйте раздел CDATA - тогда вы увидите текст без экранирования.Это лучше всего видно в примере:

Обновленное преобразование практически идентично приведенному выше, но:

  1. В директиве <xsl:output> нет method="text"1054 *
  2. Закомментированный текст является текстовым узлом <MyComment> элемента
  3. Весь «закомментированный XML» - фактически представлен в виде текста, появляется в разделе CDATA и имеет значение unescaped

Вот измененное преобразование :

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

  <xsl:template match="/">
   <MyComment>
     <xsl:value-of select="'&lt;!--&#xA;'"/>
       <xsl:apply-templates select="*" mode="escapeToText"/>
     <xsl:value-of select="'&#xA;-->'"/>
    </MyComment>
  </xsl:template>


  <xsl:template match="*" mode="escapeToText">
    <xsl:value-of select="concat('&lt;', name())"/>

    <xsl:apply-templates select="@*" mode="escapeToText"/>
    <xsl:value-of select="'>'"/>

    <xsl:apply-templates select="node()" mode="escapeToText"/>
    <xsl:value-of select="concat('&lt;/', name(), '>')"/>
  </xsl:template>

  <xsl:template match="@*" mode="escapeToText">
    <xsl:value-of select="concat(' ', name(), '=&quot;')"/>
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="string(.)"/>
    </xsl:call-template>
    <xsl:value-of select="'&quot;'"/>
  </xsl:template>

  <xsl:template match="text()" mode="escapeToText">
    <xsl:call-template name="escapeDoubleDash">
      <xsl:with-param name="pText" select="."/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="processing-instruction()" mode="escapeToText">
    <xsl:value-of select="concat('&lt;?', name(), ' ', ., ' ?>')"/>
  </xsl:template>

  <xsl:template match="comment()">
    <xsl:value-of select="concat('&lt;!CM ', ., 'CM>')"/>
  </xsl:template>

  <xsl:template name="escapeDoubleDash">
   <xsl:param name="pText"/>

   <xsl:choose>
     <xsl:when test="contains($pText, '--')">
       <xsl:value-of select="substring-before($pText, '--')"/>
       <xsl:value-of select="'!-!-'"/>
       <xsl:call-template name="escapeDoubleDash">
         <xsl:with-param name="pText" select="substring-after($pText, '--')"/>
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise><xsl:value-of select="$pText"/></xsl:otherwise>
   </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

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

<input>
    <name>Jack</name>
    <age>23</age>
    <type-10-num>1</type-10-num>
    <type-20-num>2</type-20-num>
    <type-20-char>3</type-20-char>
    <type-180-num>4</type-180-num>
    <type-180-char>5</type-180-char>
    <type-180-str>6</type-180-str>
</input>

Произведено требуемое, неэкранированное комментирование:

<MyComment><![CDATA[<!--
<input>
    <name>Jack</name>
    <age>23</age>
    <type-10-num>1</type-10-num>
    <type-20-num>2</type-20-num>
    <type-20-char>3</type-20-char>
    <type-180-num>4</type-180-num>
    <type-180-char>5</type-180-char>
    <type-180-str>6</type-180-str>
</input>
-->]]></MyComment>

Мы можем избавиться от XML-комментариев (<!-- -->) и кода, который обрабатывает «вложенные комментарии»- это больше не нужно.

...