XSLT: убедитесь, что существуют определенные элементы с содержимым по умолчанию - PullRequest
0 голосов
/ 15 июня 2011

Мне нужно убедиться, что документ XML всегда содержит следующие узлы:

<group>
    <section>0001</section>
    <head>0002</head>
    <body>0003</body>
</group>

Типичный XML-файл ввода выглядит следующим образом (узел группы всегда должен предшествовать категории и сводке):

<story>
    <group>
        <section>section-content</section>
        <head>head-content</head>
        <body>body-content</body>
        <extra>extra-content</extra>
    </group>
    <category>some text</category>
    <summary>some text</summary>
</story>

Но нет никакой гарантии, что элемент группы или любой из его дочерних элементов будет существовать.

Итак, учитывая XML-документ:

<story>
    <category>some text</category>
    <summary>some text</summary>
</story>

Вывод должен выглядеть следующим образом:

<story>
    <group>
        <section>0001</section>
        <head>0002</head>
        <body>0003</body>
    </group>
    <category>some text</category>
    <summary>some text</summary>
</story>

XSLT не должен изменять существующее содержимое, например.

<story>
    <group>
        <section>existing text</section>
        <extra>existing text</extra>
    </group>
    <category>some text</category>
    <summary>some text</summary>
</story>

Должен преобразоваться в:

<story>
    <group>
        <section>existing text</section>
        <head>0002</head>
        <body>0003</body>
        <extra>existing text</extra>
    </group>
    <category>some text</category>
    <summary>some text</summary>
</story>

Используя ответ Томалака за основу, я придумал следующее:

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

<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="utf-8" indent="yes"/>

  <!-- These defaults (elements and contents) can be modified at any time -->
  <subst:defaults>
    <subst:element name="group">
    <subst:section>0001</subst:section>
    <subst:head>0002</subst:head>
    <subst:body>0002</subst:body>
    </subst:element>
  </subst:defaults>

<!-- this makes the above available as a variable -->
<xsl:variable name="defaults" select="document('')/*/subst:defaults" />

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

<xsl:template match="story[not(group)]">
  <xsl:copy>
   <xsl:apply-templates select="@*"/>
      <xsl:element name="group"></xsl:element>
   <xsl:apply-templates select="node()"/>
  </xsl:copy>
 </xsl:template>

  <xsl:template match="group">
    <xsl:copy>
      <xsl:copy-of select="@*|*"/>
      <xsl:call-template name="create-defaults" />
    </xsl:copy>
  </xsl:template>

  <!-- Insert the defaults-->
  <xsl:template name="create-defaults">
    <xsl:variable name="this" select="." />
    <xsl:for-each select="$defaults/subst:element[@name = name($this)]/*">
      <xsl:if test="not($this/*[name() = local-name(current())])">
        <xsl:apply-templates select="." />
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

<!-- Remove the namespaces -->
  <xsl:template match="*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

Кажется, работает, если элемент группы уже существует, но я не могу понять, как заставить его работать, если элемент группы не существует.

1 Ответ

1 голос
/ 15 июня 2011

Это XSLT 1.0 преобразование

<xsl:stylesheet   version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:subst="http://tempuri.com/mysubst"
  exclude-result-prefixes="subst"
>
  <xsl:strip-space elements="*"/>
  <xsl:output method="xml" encoding="utf-8" indent="yes"/>

  <subst:defaults>
    <subst:element name="group">
      <subst:element name="section">0001</subst:element>
      <subst:element name="head">0002</subst:element>
      <subst:element name="body">0003</subst:element>
    </subst:element>
  </subst:defaults>

  <xsl:variable name="subst" select="document('')/*/subst:defaults/subst:element" />

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

  <!-- subst:element outputs a new element with the given @name -->
  <xsl:template match="subst:element">
    <xsl:element name="{@name}">
      <!-- this would also copy any additional "default" attribute! -->
      <xsl:apply-templates select="@*[not(name() = 'name')] | node()"/>
    </xsl:element>
  </xsl:template>

  <!-- only stories without any group get "special" treatment -->
  <xsl:template match="story[not(group)]">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates select="$subst[@name = 'group']" mode="copy-or-default" />
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

  <!-- a group first outputs all "default" children, then any extra children -->    
  <xsl:template match="group">
    <xsl:variable name="defaults" select="$subst[@name = 'group']/*" />
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates select="$defaults" mode="copy-or-default">
        <xsl:with-param name="parent" select="." />
      </xsl:apply-templates>
      <!-- any elements that don't have a default AND any non-element children -->
      <xsl:apply-templates select="
        *[not(name() = $defaults/@name)] | node()[not(self::*)]
      " />
    </xsl:copy>
  </xsl:template>

  <!-- in "copy-or-default" mode, this checks the context parent
       and either copies the element from there or uses the default --> 
  <xsl:template match="subst:element" mode="copy-or-default">
    <xsl:param name="parent" />

    <xsl:choose>
      <xsl:when test="$parent and $parent/*[name() = current()/@name]">
        <xsl:apply-templates select="$parent/*[name() = current()/@name]" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

применительно к этому входу

<x>
  <story>
    <group>
      <section>0001</section>
      <head>0002</head>
      <body>0003</body>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
  <story>
    <group>
      <body>0004</body>
      <!-- a comment! -->
      <foo>blah</foo>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
  <story>
    <category>some text</category>
    <summary>some text</summary>
  </story>
</x>

производит:

<x>
  <story>
    <group>
      <section>0001</section>
      <head>0002</head>
      <body>0003</body>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
  <story>
    <group>
      <section>0001</section>
      <head>0002</head>
      <body>0004</body>
      <!-- a comment! -->
      <foo>blah</foo>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
  <story>
    <group>
      <section>0001</section>
      <head>0002</head>
      <body>0003</body>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
</x>
...