【问题标题】:XSLT Grouping flat XMLXSLT 分组平面 XML
【发布时间】:2014-03-10 19:19:37
【问题描述】:

我有一个来自数据库的平面 XML 文件。我需要使用 XSL 转换将数据分组到更分层的布局中。我已经进行了很多研究,并提出了 Meunchian 分组作为要走的路,但无法让它发挥作用。

我要去:

<Report>
<Data>
    <Row>
        <Field name ="AssessmentID">1</Field>
        <Field name ="Company">Test Company</Field>
        <Field name ="Manager">Bob Smith</Field>
        <Field name ="IssueID">1-1</Field>
        <Field name ="IssueTitle">Security Problem</Field>
        <Field name ="IssueDescription">Some Description</Field>
    </Row>
    <Row>
        <Field name ="AssessmentID">1</Field>
        <Field name ="Company">Test Company</Field>
        <Field name ="Manager">Bob Smith</Field>
        <Field name ="IssueID">1-2</Field>
        <Field name ="IssueTitle">Other Problem</Field>
        <Field name ="IssueDescription">Some Other Description</Field>
    </Row>
</Data>
</Report>

到这里:

<Assessments>
<Assessment>
    <AssessmentID>1</AssessmentID>
    <Company>Test Company</Company>
    <Manager>Bob Smith</Manager>
    <Issue>
        <IssueID>1-1</IssueID>
        <IssueTitle>Security Problem</IssueTitle>
        <IssueDescription>Some Description</IssueDescription>
    </Issue>
    <Issue>
        <IssueID>1-2</IssueID>
        <IssueTitle>Other Problem</IssueTitle>
        <IssueDescription>Some Other Description</IssueDescription>
    </Issue>
</Assessment>
</Assessments>

这是我目前想出的代码:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
        <xsl:key name="keyAssessmentID" match="Row" use="Field[@name='AssessmentID']"/>
        <xsl:key name="keyIssueID" match="Row" use="Field[@name='IssueID']"/>
    <xsl:template match="/">
        <Assessments>
            <!-- Process each Assessment -->
        <xsl:for-each select="//Row[generate-id(.) = generate-id(key('keyAssessmentID', Field[@name='AssessmentID'])[1])]">
                <!-- Select all the issues belonging to the assessment -->
                    <xsl:variable name ="lngAssessmentID"><xsl:value-of select="Field[@name='AssessmentID']" /></xsl:variable>
                    <xsl:variable name="lstIssue" select="//Row[Field[@name='IssueID']=$lngAssessmentID]" />

                    <!-- show details for Issues in Assessments -->
                    <xsl:call-template name="ShowIssuesInAssessment">
                        <xsl:with-param name="lstIssue" select="$lstIssue" />
                    </xsl:call-template>
        </xsl:for-each>             
        </Assessments>
    </xsl:template>

    <xsl:template name="ShowIssuesInAssessment">
        <xsl:param name="lstIssue" />

        <!-- Show the name of the Assessment currently being processed -->
        <AssessmentID>
            <xsl:value-of select="$lstIssue[1]/Field[@name='AssessmentID']" />
        </AssessmentID>

 <!-- Show IssueID for each Issue in the Assessment -->
 <xsl:for-each select="$lstIssue[generate-id(.) = generate-id(key('keyIssueID', Field[@Name='IssueID'])[1])]">
  <xsl:variable name="lngIssueID" select="Field[@Name='IssueID']" />
  <!-- Show details of each Issue -->
  <Issue>
    <IssueID>
        <xsl:value-of select="$lstIssue[Field[@Name='IssueID']=$lngIssueID]/Field[@Name='IssueID']" />
     </IssueID>
     <IssueTitle>
            <xsl:value-of select="$lstIssue[Field[@Name='IssueID']=$lngIssueID]/Field[@Name='IssueTitle']" />
   </IssueTitle>
   </Issue>
     </xsl:for-each>
     </xsl:template>
    </xsl:stylesheet>

我正在编写 CodeProject 上的这个示例:here

感谢大家的帮助!

【问题讨论】:

  • 您无法开始工作的 XSLT 代码在哪里
  • OJay,这是我的第一篇 StackOverflow 帖子,感谢您对礼仪的指导。我发布了 XSL 和我一直在使用的示例。
  • 您需要对 IssueIDAssessmentID 进行分组吗?也就是说,对于给定的 AssessmentID,您能否在文件中多次出现相同的 IssueID
  • 问题 ID 对于每个问题都是唯一的,不会在给定的评估中重复。

标签: xml xslt xml-parsing


【解决方案1】:

使用 Muenchian 分组获取每个 AssessmentID

的第一次出现,您已经正确地开始了
<xsl:for-each select="//Row[generate-id(.) = generate-id(key('keyAssessmentID', Field[@name='AssessmentID'])[1])]">

但要获得评估的所有“问题”,您实际上应该使用密钥(其中 $AssessmentId 是包含 AssessmentId 的变量)

<xsl:apply-templates select="key('keyAssessmentID', $AssessmentId)"/>

我看不出有什么需要在这里使用xsl:call-template,或者将组中的元素作为参数传入。只需使用模板匹配,这是 XSLT 擅长的。然后在匹配Row的模板中,可以输出问题详情

<xsl:template match="Row">
   <Issue>
      <xsl:apply-templates select="Field[@name='IssueID']"/>
      <xsl:apply-templates select="Field[@name='IssueTitle']"/>
      <xsl:apply-templates select="Field[@name='IssueDescription']"/>
   </Issue>
</xsl:template>

为了节省代码重复,您可以使用一个模板匹配这些变量字段

<xsl:template match="Row/*">
   <xsl:element name="{@name}">
      <xsl:value-of select="."/>
   </xsl:element>
</xsl:template>

(这利用“属性值模板”根据字段的 @name 属性值创建元素名称)。

试试这个 XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
   <xsl:key name="keyAssessmentID" match="Row" use="Field[@name='AssessmentID']"/>

   <xsl:template match="/*">
      <Assessments>
         <xsl:for-each select=".//Row[generate-id(.) = generate-id(key('keyAssessmentID', Field[@name='AssessmentID'])[1])]">
            <xsl:variable name="AssessmentId" select="Field[@name='AssessmentID']"/>
            <Assessment>
               <AssessmentID>
                  <xsl:value-of select="$AssessmentId"/>
               </AssessmentID>
               <xsl:apply-templates select="key('keyAssessmentID', $AssessmentId)"/>
            </Assessment>
         </xsl:for-each>
      </Assessments>
   </xsl:template>

   <xsl:template match="Row">
      <Issue>
         <xsl:apply-templates select="Field[@name='IssueID']"/>
         <xsl:apply-templates select="Field[@name='IssueTitle']"/>
         <xsl:apply-templates select="Field[@name='IssueDescription']"/>
      </Issue>
   </xsl:template>

   <xsl:template match="Row/*">
      <xsl:element name="{@name}">
         <xsl:value-of select="."/>
      </xsl:element>
   </xsl:template>
</xsl:stylesheet>

然后,阅读 http://www.jenitennison.com/xslt/grouping/muenchian.html 以更好地了解 Muenchian Grouping。

【讨论】:

  • 非常感谢 Tim C 的帮助。我非常感谢关于其工作原理的解释,它确实有助于提高我对转换逻辑的理解。
【解决方案2】:

@Tim C 的精彩回答,但我想我会以稍微不同的角度添加我的答案。我认为关键是您似乎也在那里使用了 keyIssueID 键,这并不是真正需要的,根据您建议的输出,您只需要拥有 AssessmentID 的键

<?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:key name="keyAssessmentID" match="Row" use="./Field[@name='AssessmentID']"/>
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/">
    <xsl:element name="Assessments">
      <xsl:for-each select="//Row[generate-id(.) = generate-id(key('keyAssessmentID',Field[@name='AssessmentID']))]">
        <xsl:element name="Assesment">
          <xsl:variable name="AssessmentId" select="./Field[@name='AssessmentID']" />
          <xsl:element name="AssessmentId">
            <xsl:value-of select="$AssessmentId"/>
          </xsl:element>
          <xsl:element name="Company">
            <xsl:value-of select="./Field[@name='Company']"/>
          </xsl:element>
          <xsl:element name="Manager">
            <xsl:value-of select="./Field[@name='Manager']"/>
          </xsl:element>
          <xsl:for-each select="key(keyAssessmentID,$AssessmentId)">
            <xsl:element name="Issue">
              <xsl:for-each select="following-sibling::Field[contains(@name,'Issue')]">
                <xsl:element name="{./@name}">
                  <xsl:value-of select="."/>
                </xsl:element>
              </xsl:for-each>
            </xsl:element>
          </xsl:for-each>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

【讨论】:

  • OJay,我非常感谢替代解决方案。我是 XSL 转换的新手,这对拆开并弄清楚它是如何工作的非常有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-22
  • 1970-01-01
相关资源
最近更新 更多