Преобразование XSLT из XML в CSV для доступа к вложенным элементам с тем же именем

Я пытаюсь преобразовать приведенный ниже образец XML в CSV, но у меня возникают трудности с сопоставлением вложенных элементов с тем же именем (Правило).

Какое преобразование XSLT может генерировать эту структуру?

Файл @ path = "filename1.txt" | Правило @ id = "3.1.6" | Сообщение @ severity = "3" | Сообщение @ text = "3480. ....."

Файл @ path = "filename1.txt" | Правило @ id = "3.5.19" | Сообщение @ severity = "3" | Сообщение @ text = "1281. ....."

Файл @ path = "filename2.txt" | Правило @ id = "3.1.6" | Сообщение @ severity = "3" | Сообщение @ text = "3480. ....."

Файл @ path = "filename2.txt" | Правило @ id = "3.5.3" | Сообщение @ severity = "3" | Сообщение @ text = "3219. ....."

Путь будет выглядеть так:

      AnalysisData\dataroot type="per-file"\File\tree type="rules"\RuleGroup name="MISRA_C"\...\Rule id="[1-9]+\.[1-9]+\.[1-9]+"\Message

Входной XML:

      <AnalysisData>
  <dataroot type="project">
  </dataroot>
  <dataroot type="per-file">
    <File path="filename1.txt">
      <Json>1.json</Json>
      <tree type="rules">
        <RuleGroup name="MISRA_C" total="2" active="2" >
          <Rule id="3" total="2" active="2" text="Mandatory" >
            <Rule id="3.1" total="1" active="1" text="Common" >
              <Rule id="3.1.6" total="1" active="1" text="Declarations and definitions" >
                <Message guid="qac-9.6.0-3480" total="1" active="1" severity="3" text="3480.  Object/function '%s', with internal linkage, has been defined in a header file." />
              </Rule>
            </Rule>
            <Rule id="3.5" total="1" active="1" text="MISRA Required Rules" >
              <Rule id="3.5.19" total="1" active="1" text="M3CM Rule-7.2 A &quot;u&quot; or &quot;U&quot; suffix shall be applied to all integer constants that are represented in an unsigned type" >
                <Message guid="qac-9.6.0-1281" total="1" active="1" severity="3" text="1281.  Integer literal constant is of an unsigned type but does not include a &quot;U&quot; suffix." />
              </Rule>
            </Rule>
          </Rule>
        </RuleGroup>
      </tree>
    </File>
    <File path="filename2.txt">
      <Json>2.json</Json>
      <tree type="rules">
        <RuleGroup name="CrossModuleAnalysis" total="11" active="11" >
          <Rule id="1" total="11" active="11" text="Maintainability" >
            <Rule id="1.1" total="11" active="11" text="CMA Declaration Standards" >
              <Message guid="rcma-2.0.0-1534" total="11" active="11" severity="2" text="1534.  The macro '%1s' is declared but not used within this project." />
            </Rule>
          </Rule>
        </RuleGroup>
        <RuleGroup name="MISRA_C" total="36" active="16" >
          <Rule id="3" total="20" active="0" text="Mandatory" >
            <Rule id="3.1" total="12" active="0" text="Common" >
              <Rule id="3.1.6" total="12" active="0" text="Declarations and definitions" >
                <Message guid="qac-9.6.0-3480" total="12" active="0" severity="3" text="3480.  Object/function '%s', with internal linkage, has been defined in a header file." />
              </Rule>
            </Rule>
            <Rule id="3.5" total="8" active="0" text="MISRA Required Rules" >
              <Rule id="3.5.3" total="8" active="0" text="M3CM Rule-2.1 A project shall not contain unreachable code" >
                <Message guid="qac-9.6.0-3219" total="8" active="0" severity="3" text="3219.  Static function '%s()' is not used within this translation unit." />
              </Rule>
            </Rule>
          </Rule>
          <Rule id="2" total="16" active="16" text="Minor" >
            <Rule id="2.1" total="16" active="16" text="Common" >
              <Rule id="2.1.15" total="16" active="16" text="Declarations and Definitions" >
                <Message guid="qac-9.6.0-3227" total="16" active="16" severity="2" text="3227.  The parameter '%s' is never modified and so it could be declared with the 'const' qualifier." />
              </Rule>
            </Rule>
          </Rule>
        </RuleGroup>
      </tree>
    </File>
  </dataroot>
</AnalysisData>

1 ответ

Решение

Обычно я предлагаю написать шаблон для элемента, отображаемого в строке, а затем использовать xsl:value-of separator чтобы вывести строку с выбранным разделителем:

      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="text"/>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:apply-templates 
      select="/AnalysisData/dataroot[@type = 'per-file']/File/tree/RuleGroup[@name = 'MISRA_C']//Rule[Message]"/>
  </xsl:template>
  
  <xsl:template match="Rule">
    <xsl:value-of select="ancestor::File/@path, @id, Message!(@severity, @text)" separator=" | "/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

Если вы хотите вывести значения атрибутов плюс имя элемента / атрибута, тогда может оказаться полезной функция:

      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="text"/>
  
  <xsl:function name="mf:line" as="xs:string*">
    <xsl:param name="atts" as="attribute()*"/>
    <xsl:sequence
      select="$atts ! (local-name(..) || '@' || local-name() || '=&quot;' || . || '&quot;')"/>
  </xsl:function>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:apply-templates 
      select="/AnalysisData/dataroot[@type = 'per-file']/File/tree/RuleGroup[@name = 'MISRA_C']//Rule[Message]"/>
  </xsl:template>
  
  <xsl:template match="Rule">
    <xsl:value-of select="mf:line((ancestor::File/@path, @id, Message!(@severity, @text)))" separator=" | "/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>

</xsl:stylesheet>
Другие вопросы по тегам