【问题标题】:How can I make XSL produce the following result given the input XML给定输入 XML,如何使 XSL 产生以下结果
【发布时间】:2012-01-11 16:37:56
【问题描述】:

我尝试了几件事...使用变量和模板,但进展缓慢。但我只是没有让这个工作 - 我的上下文有问题。

输入...

  • 表“700”可以包含多个条目。
  • Subject1、Subject2、Subject3 的值将是 English、Math、Science(但顺序可能因源 XML 不同而异)
  • 分数在位置上与主题相关(即 Score1 适用于 Subject1)

输出...

  • 将始终包含 6 个节点(英语、数学、科学、类、类 2、类 3)
  • Subject_ 标签顺序始终需要是英语、数学,然后是科学。
  • Subject_ 标签将大写
  • 如果对应的分数 > 0,Subject_ 标签将包含标志 1;否则 0
  • Class_Score 标记顺序始终需要是英语、数学,然后是科学。

我修改了我的代码,以下可能无法完全正常运行,但可以让我了解我的尝试。

我的第一个问题是 - 我在正确的轨道上吗?

  • 通过科目到成绩模板
  • 捕获主题的“索引”
  • 将主题和索引传递给 add-Grades-nodes 模板

这就是我的上下文问题阻止我的地方。

-- 我的输入 XML--

<?xml version="1.0"?>
<Account Number="123456">
  <Data>
    <Table ID="700">
      <Record ID="1" SubClass="Person">
        <Name.Last>Smith</Name.Last>
        <Name.First>John</Name.First>
        <Score1>50</Score1>
        <Score2>75</Score2>
        <Score3>100</Score3>
        <Subject1>Math</Subject1>
        <Subject2>English</Subject2>
        <Subject3>Science</Subject3>
      </Record>
      <Record ID="2" SubClass="Person">
        <Name.Last>Smith</Name.Last>
        <Name.First>Jane</Name.First>
        <Score1></Score1>
        <Score2>77</Score2>
        <Score3>80</Score3>
        <Subject1>Math</Subject1>
        <Subject2>English</Subject2>
        <Subject3>Science</Subject3>
      </Record>
    </Table>
  </Data>
</Account>

-- 所需的输出 XML--

<Out>
    <Subject_ENGLISH>1</Subject_ENGLISH>
    <Subject_MATH>1</Subject_MATH>
    <Subject_SCIENCE>1</Subject_SCIENCE>
    <Class_SCORE>75</Class_SCORE>
    <Class2_SCORE>50</Class2_SCORE>
    <Class3_SCORE>100</Class3_SCORE>
    <Subject_ENGLISH>0</Subject_ENGLISH>
    <Subject_MATH>1</Subject_MATH>
    <Subject_SCIENCE>1</Subject_SCIENCE>
    <Class_SCORE></Class_SCORE>
    <Class2_SCORE>77</Class2_SCORE>
    <Class3_SCORE>80</Class3_SCORE>
</Out>

-- 当前 XSLT 不起作用--

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0">
  <xsl:output method="xml" encoding="UTF-8"/>

  <xsl:template match="Account">
    <Out>
      <xsl:for-each select="/Account/Data/Table[@ID='700']/Record[@SubClass='Person']>

        <xsl:call-template name="Grades">
          <xsl:with-param name="Subject">English</xsl:with-param>
        </xsl:call-template>
        <xsl:call-template name="Grades">
          <xsl:with-param name="Subject">Math</xsl:with-param>
        </xsl:call-template>
        <xsl:call-template name="Grades">
          <xsl:with-param name="Subject">Science</xsl:with-param>
        </xsl:call-template>


      </xsl:for-each>
    </Out>
  </xsl:template>


  <xsl:template name="Grades">
    <xsl:param name="Subject"/>

    <xsl:for-each select="*[starts-with(name(), 'Subject')][node()=$Subject]">

      <xsl:variable name='cr-index'>
        <xsl:value-of select ='substring(name(), string-length(name()))'/>
      </xsl:variable>

      <xsl:call-template name="create-Grades-nodes">
        <xsl:with-param name="cr-context" select =".."/>
        <xsl:with-param name="Subject">
          <xsl:value-of select='$Subject' />
        </xsl:with-param>
        <xsl:with-param name="cr-index">
          <xsl:value-of select='$cr-index'/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:for-each>
  </xsl:template>

  <xsl:template name="create-Grades-nodes" match="$cr-context" >
    <xsl:param name ="cr-context"/>
    <xsl:param name ="Subject"/>
    <xsl:param name ="cr-index"/>

    <xsl:variable name='cr-score'>
      <xsl:value-of select='name($cr-context)/concat("Score", $cr-index)'/>
    </xsl:variable>

    <xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
    <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />

      <xsl:element name='{concat("Subject_", translate($Subject, $smallcase, $uppercase))}'>
        <xsl:choose>
        <xsl:when test= '$cr-score &gt; 0'>
          <xsl:value-of select = "1"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select = "0"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:element>

  </xsl:template>
</xsl:stylesheet>

【问题讨论】:

  • 节点是否总是有序的?换句话说,你会得到 5075100 还是 MathEnglish科学 ?
  • 我无法保证订单,但我希望它们会按顺序排列。我使用的 xml 是由外部供应商生产的。
  • 您尝试的 XSL 当前输出的是什么,与您希望它输出的相反?换句话说,您希望输出的哪个方面有问题?
  • 我可以从 Subject# 节点中获取数字。但无法到对应的Score#节点判断是否有值。

标签: xml xslt


【解决方案1】:

这种转变:

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

 <xsl:param name="pSubjects">
  <s name="English" at="1"/>
  <s name="Math"    at="2"/>
  <s name="Science" at="3"/>
 </xsl:param>

 <xsl:variable name="vSubjects" select=
 "document('')/*/xsl:param[@name='pSubjects']/*"/>

 <xsl:variable name="vLower" select=
  "'abcdefghijklmnopqrstuvwxyz'"/>

 <xsl:variable name="vUpper" select=
  "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>

 <xsl:template match="/*">
  <Out>
   <xsl:apply-templates/>
  </Out>
 </xsl:template>

 <xsl:template match="Table[@ID='700']/Record">
  <xsl:apply-templates select="*[starts-with(name(), 'Su')]">
    <xsl:sort select="$vSubjects[@name = current()]/@at" data-type="number"/>
  </xsl:apply-templates>

  <xsl:apply-templates select="*[starts-with(name(), 'Sc')]">
   <xsl:sort select=
    "$vSubjects[@name
               = current()/../*[starts-with(name(), 'Su')]
                    [substring-after(name(), 'Subject')
                    =
                     substring-after(name(current()), 'Score')
                    ]
                ]/@at"
     data-type="number"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="*[starts-with(name(), 'Su')]">
   <xsl:variable name="vgenName" select=
   "concat('Subject_',
            translate(., $vLower, $vUpper)
           )"/>
  <xsl:element name="{$vgenName}">
    <xsl:value-of select="."/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="*[starts-with(name(), 'Sc')]">
  <xsl:variable name="vInd" select=
   "$vSubjects[@name
               = current()/../*[starts-with(name(), 'Su')]
                    [substring-after(name(), 'Subject')
                    =
                     substring-after(name(current()), 'Score')
                    ]
                ]/@at"/>

  <xsl:variable name="vgenName" select=
   "concat('Class',
           translate($vInd, '1', ''),
           '_SCORE'
           )
   "/>
   <xsl:element name="{$vgenName}">
    <xsl:value-of select="."/>
   </xsl:element>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<Account Number="123456">
  <Data>
    <Table ID="700">
      <Record ID="1" SubClass="Person">
        <Name.Last>Smith</Name.Last>
        <Name.First>John</Name.First>
        <Score1>50</Score1>
        <Score2>75</Score2>
        <Score3>100</Score3>
        <Subject1>Math</Subject1>
        <Subject2>English</Subject2>
        <Subject3>Science</Subject3>
      </Record>
      <Record ID="2" SubClass="Person">
        <Name.Last>Smith</Name.Last>
        <Name.First>Jane</Name.First>
        <Score1></Score1>
        <Score2>77</Score2>
        <Score3>80</Score3>
        <Subject1>Math</Subject1>
        <Subject2>English</Subject2>
        <Subject3>Science</Subject3>
      </Record>
    </Table>
  </Data>
</Account>

产生想要的正确结果

<Out>
  <Subject_ENGLISH>English</Subject_ENGLISH>
  <Subject_MATH>Math</Subject_MATH>
  <Subject_SCIENCE>Science</Subject_SCIENCE>
  <Class_SCORE>75</Class_SCORE>
  <Class2_SCORE>50</Class2_SCORE>
  <Class3_SCORE>100</Class3_SCORE>
  <Subject_ENGLISH>English</Subject_ENGLISH>
  <Subject_MATH>Math</Subject_MATH>
  <Subject_SCIENCE>Science</Subject_SCIENCE>
  <Class_SCORE>77</Class_SCORE>
  <Class2_SCORE></Class2_SCORE>
  <Class3_SCORE>80</Class3_SCORE>
</Out>

【讨论】:

  • 非常感谢。您确实教会了我一些使用 XSLT 的巧妙技巧。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-09
相关资源
最近更新 更多