Output values in a certain way using XSLT/XPath 2.0

隐身守侯 提交于 2019-12-11 03:16:57

问题


I have an XML like this:

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

<Section>
    <Chapter>
        <Cell colname="1">
            <Value>A</Value>
        </Cell>
        <Cell colname="2">
            <MyValue>AAA</MyValue>
            <MyValue>BBB</MyValue>
        </Cell>
        <Cell colname="3">
            <MyCar>Honda</MyCar>
        </Cell>
    </Chapter>
    <Chapter>
        <Cell colname="1">
            <Value>C</Value>
        </Cell>
        <Cell colname="2">
            <MyValue>CCC</MyValue>
        </Cell>
        <Cell colname="3">
            <MyCar>Toyota</MyCar>
        </Cell>
    </Chapter>
</Section>

I like the have a message (later on convert them tags) output like this:

A AAA Honda A BBB Honda C CCC Toyota

This is my XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output method="xml" version="1.0" encoding="iso-8859-1"   indent="yes"/>

    <xsl:template match="/">
        <xsl:apply-templates select="Section//Chapter"/>
    </xsl:template>

    <xsl:template match="Chapter">
        <xsl:for-each select="Cell[@colname='2']//MyValue">
            <xsl:message>
                <xsl:value-of select="Cell[@colname='1']/Value"/>
                <xsl:value-of select="."/>
                <xsl:value-of select="Cell[@colname='3']/MyCar"/>
            </xsl:message>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="text()" />

</xsl:stylesheet>

Unfortunately it doesn't output what I'd like it to do :(.

I realize that for-each will change the context so the remaining value-ofs won't do anything.

What would be a solution for this ?.

TIA,

John


回答1:


This XSLT 2.0 transformation (the equivalent XSLT 1.0 transformation can be mechanically written from this one).

Do note: This is a generic solution that works with any number of children and doesn't rely on hardcoded names.

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

 <xsl:template match="Chapter">
  <xsl:apply-templates select="Cell[1]"/>
 </xsl:template>

 <xsl:template match="Cell">
  <xsl:param name="pText" as="xs:string*"/>

  <xsl:apply-templates select="*[1]">
   <xsl:with-param name="pText" select="$pText"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="Cell/*">
  <xsl:param name="pText" as="xs:string*"/>

  <xsl:variable name="vText" as="xs:string*"
   select="$pText, string(.)"/>

  <xsl:sequence select=
   "$vText
      [not(current()/../following-sibling::Cell)]"/>
  <xsl:apply-templates select="../following-sibling::Cell[1]">
    <xsl:with-param name="pText" select="$vText"/>
  </xsl:apply-templates>
  <xsl:apply-templates select="following-sibling::*">
    <xsl:with-param name="pText" select="$pText"/>
  </xsl:apply-templates>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<Section>
    <Chapter>
        <Cell colname="1">
            <Value>A</Value>
        </Cell>
        <Cell colname="2">
            <MyValue>AAA</MyValue>
            <MyValue>BBB</MyValue>
        </Cell>
        <Cell colname="3">
            <MyCar>Honda</MyCar>
        </Cell>
    </Chapter>
    <Chapter>
        <Cell colname="1">
            <Value>C</Value>
        </Cell>
        <Cell colname="2">
            <MyValue>CCC</MyValue>
        </Cell>
        <Cell colname="3">
            <MyCar>Toyota</MyCar>
        </Cell>
    </Chapter>
</Section>

produces exactly the wanted, correct result:

A AAA Honda A BBB Honda C CCC Toyota

Explanation:

  1. This is essentially a task for producing all combinations of values that belong to N groups (the children of a Cell make a group), taking one item from each group.

  2. At any item of group K, we add this item to the current combination of items of the groups 1, 2, ..., K-1. Then we pass this newly formed combination to the group K+1. If we are an item in the last group (N), we print out (xsl:sequence) the whole accumulated combination.

  3. When the control returns, all combinations of elements of the remaining groups (K+1, K+2, ..., N) have been appended to our current combination of the group items of groups 1, 2, ..., K. All these combinations have already been printed.

  4. We pass the same group elements combination that was passed to us -- now we pass it to the following item in the current group (the following-sibling).




回答2:


I have modified your approach a little, because I'm guessing that you really don't want to use <xsl:message> diagnostic operation. Besides that, I'm not generating XML on the output and I'm not using for-each:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="text" version="1.0" encoding="iso-8859-1"   indent="yes"/>
    <xsl:template match="/Section">
        <xsl:apply-templates select="Chapter"/>
    </xsl:template>
    <xsl:template match="Chapter">
        <xsl:apply-templates select="Cell/MyValue" />
    </xsl:template>
    <xsl:template match="MyValue">
            <xsl:value-of select="../../Cell[@colname='1']/Value/text()"/>
            <xsl:text> </xsl:text>
            <xsl:value-of select="."/>
            <xsl:text> </xsl:text>
            <xsl:value-of select="../../Cell[@colname='3']/MyCar"/>
            <xsl:text> </xsl:text>
    </xsl:template>
</xsl:stylesheet>


来源:https://stackoverflow.com/questions/9448446/output-values-in-a-certain-way-using-xslt-xpath-2-0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!