开发者

Xsl transformation question

Next question: I have "InputDoc" xml document and xslt file in order to transform into other "OutputDoc" xml document. You can find examples xslt and xml documents below.

<?xml version="1.0" encoding="UTF-8"?>
<InputDoc>
  <InputCollection>
    <InputItem>
      <InputValue>Value_1</InputValue>
    </InputItem>
  </InputCollection>
</InputDoc>

<?xml version="1.0" encoding="utf-16"?>
<OutputDoc>
  <OutputE开发者_高级运维lement>
    <OutputItem>
      <OutputValue>Value_1</OutputValue>
    </OutputItem>
    <OutputDescription>Description_1</OutputDescription>
  </OutputElement>
</OutputDoc>

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="//InputCollection">
    <OutputDoc>
      <xsl:for-each select="InputItem">
        <OutputElement>
          <OutputItem>
            <OutputValue><xsl:value-of select="InputValue" /></OutputValue>
          </OutputItem>
          <OutputDescription>
            <xsl:call-template name="InputValue2OutputDescriptionMappings">
              <xsl:with-param name="InputValueParam" select="InputValue" />
            </xsl:call-template>
          </OutputDescription>
        </OutputElement>
      </xsl:for-each>
    </OutputDoc>
  </xsl:template>

  <xsl:template name="InputValue2OutputDescriptionMappings">
    <xsl:param name="InputValueParam" />
        <xsl:choose>
            <xsl:when test="$InputValueParam='Value_1'">Description_1</xsl:when>
            <xsl:when test="$InputValueParam='Value_2'">Description_2</xsl:when>
            <xsl:otherwise></xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

It works. But It would be nice if output xml document contained just "OutputElement" nodes which contains values into "InputValue2OutputDescriptionMappings", i.e. if value of "OutputDescription" node is empty then "OutputElement" node won't be included into "OutputDoc".

How I can do it using XSL transformation above?


This is an standard XSLT 1.0 stylesheet optimized with key():

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="description" match="description" use="@value"/>
    <xsl:variable name="map">
        <description value="Value_1">Description_1</description>
        <description value="Value_2">Description_2</description>
    </xsl:variable>
    <xsl:template match="InputDoc">
        <OutputDoc>
            <xsl:apply-templates/>
        </OutputDoc>
    </xsl:template>
    <xsl:template match="InputCollection">
        <xsl:variable name="me" select="."/>
        <xsl:for-each select="document('')">
            <xsl:if test="key('description',$me/InputItem/InputValue)">
                <OutputElement>
                    <xsl:apply-templates select="$me/*"/>
                    <xsl:apply-templates select="key('description',$me/InputItem/InputValue)"/>
                </OutputElement>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="InputItem">
        <OutputItem>
            <xsl:apply-templates/>
        </OutputItem>
    </xsl:template>
    <xsl:template match="InputValue">
        <OutputValue>
            <xsl:apply-templates/>
        </OutputValue>
    </xsl:template>
    <xsl:template match="description">
        <OutputDescription>
            <xsl:apply-templates/>
        </OutputDescription>
    </xsl:template>
</xsl:stylesheet>

Result:

<OutputDoc>
    <OutputElement>
        <OutputItem>
            <OutputValue>Value_1</OutputValue>
        </OutputItem>
        <OutputDescription>Description_1</OutputDescription>
    </OutputElement>
</OutputDoc>

Note: "Pattern matching". key() with multiple documents (this case, the stylesheet).

Edit: Miss strip OutputElement for non matching value request, sorry.

Now, this input:

<InputDoc>
  <InputCollection>
    <InputItem>
      <InputValue>Value_3</InputValue>
    </InputItem>
  </InputCollection>
</InputDoc>

Output:

<OutputDoc></OutputDoc>

Note: This is all template driven. So, you can apply more logic even to description content.


You have to get the result of the mapping first, THEN test it for empty and omit the <OutputDescription> tag, as in:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="//InputCollection">
    <OutputDoc>
      <xsl:for-each select="InputItem">
        <OutputElement>
          <OutputItem>
            <OutputValue><xsl:value-of select="InputValue" /></OutputValue>
          </OutputItem>
          <xsl:variable name="desc">
            <xsl:call-template name="InputValue2OutputDescriptionMappings">
              <xsl:with-param name="InputValueParam" select="InputValue" />
            </xsl:call-template>
          </xsl:variable>
          <xsl:if test="string-length($desc/text())">
            <OutputDescription><xsl:value-of select="$desc"/></OutputDescription>
          </xsl:if>
        </OutputElement>
      </xsl:for-each>
    </OutputDoc>
  </xsl:template>

  <xsl:template name="InputValue2OutputDescriptionMappings">
    <xsl:param name="InputValueParam" />
    <xsl:choose>
      <xsl:when test="$InputValueParam='Value_1'">Description_1</xsl:when>
      <xsl:when test="$InputValueParam='Value_2'">Description_2</xsl:when>
      <xsl:otherwise></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

However, here is a much more XSL-esque version that stores the mappings in a variable to dispense with the mapping template, and uses "push" processing (apply-templates) instead of "pull" processing.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="//InputCollection">
    <OutputDoc>
      <xsl:apply-templates select="InputItem"/>
    </OutputDoc>
  </xsl:template>

  <xsl:variable name="mapping">
    <map input="Value_1" output="Description_1"/>
    <map input="Value_2" output="Description_2"/>
  </xsl:variable>

  <xsl:template match="InputItem">
    <OutputElement>
      <OutputItem>
        <OutputValue><xsl:value-of select="InputValue" /></OutputValue>
      </OutputItem>
      <xsl:variable name="input" select="InputValue/text()"/>
      <xsl:variable name="desc" select="$mapping/map[@input=$input]/@output"/>
      <xsl:if test="$desc">
        <OutputDescription><xsl:value-of select="$desc"/></OutputDescription>
      </xsl:if>
    </OutputElement>
  </xsl:template>

</xsl:stylesheet>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜