从另一个文件查找值进行XSLT求和

4
我想从以下两个XML文件中获取特定学生的总分:
--- 文件:mark.xml ---
<marks>
  <mark type="HD">10</mark>
  <mark type="D">8</mark>
  <mark type="C">5</mark>
</marks>

--- 文件: studentRecord.xml ---
<students>
  <student id="1234">
    <grade>HD</grade>
  </student>
  <student id="1234">
    <grade>C</grade>
  </student>
  <student id="1111">
    <grade>D</grade>
  </student>
</students>

如何获取ID为1234的学生的总分?应该是15分。

1
你正在使用哪种编程环境/技术? - Tao
XSLT可以进行多文件查询吗?我认为你需要使用XQuery(或某个宿主环境)来实现。将这两个文档合并成一个,这样做是否可行? - Donal Fellows
好问题。+1。请查看我的答案,其中包含一个完整、简短、易于理解的XSLT解决方案,实际上只是一个一行的XPath表达式。 - Dimitre Novatchev
我的回答对您有用吗?考虑接受最佳答案了吗? - Dimitre Novatchev
3个回答

3

这里有一个简短易懂的XSLT解决方案(其实只是XPath):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
 <xsl:output method="text"/>

 <my:MarkValues>
        <marks>
            <mark type="HD">10</mark>
            <mark type="D">8</mark>
            <mark type="C">5</mark>
        </marks>
 </my:MarkValues>

 <xsl:template match="/*">
     <xsl:value-of select=
     "sum(document('')/*
           /my:MarkValues/*/*
               [@type = current()/*
                          [@id='1234']/grade
             ]
        )"/>
 </xsl:template>
</xsl:stylesheet>

当这个转换应用于提供的 XML 文档(名为“studentRecord.xml”)时:

<students>
    <student id="1234">
        <grade>HD</grade>
    </student>
    <student id="1234">
        <grade>C</grade>
    </student>
    <student id="1111">
        <grade>D</grade>
    </student>
</students>

所需的答案已生成:

15

如果您想将分数值保存在单独的文件中(而不是像上面那样嵌入到XSLT样式表中),XPath表达式应该稍作修改(只需更改document()函数的参数即可:
sum(document('mark.xml')/*/*
                     [@type = current()/*
                                     [@id='1234']/grade
                     ]
   )

解释:

  1. 使用XSLT的document()函数。

  2. 使用XSLT的current()函数。

  3. 使用XPath的sum()函数。


感谢这个XPath技巧。我一直在想是否有可能实现它。这就是为什么我的答案略有不同(也许有点傻)。如果您有时间和兴趣,我想知道您对这个其他问题的看法(或答案)question。再次感谢您分享您的知识。 - Emiliano Poggi
@empo:不用谢。希望你不会讨厌我对你的解决方案的评论... :) - Dimitre Novatchev

0
在下面的转换中,我首先使用收集到的值构建一个变量,然后使用XPath的sum()函数来获取结果。这种方法不像@Dimitre的方式那样优雅和智能,但我还是想发布一下 :)

XSLT 2.0Saxon-HE 9.2.1.1J 下进行了测试。

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

    <xsl:variable name="lookup" select="document('lookup.xml')/marks"/> 

    <xsl:template match="students">

        <xsl:variable name="values">
            <values>
                <xsl:for-each select="student[@id='1234']">
                    <value><xsl:value-of select="$lookup/mark[@type=current()/grade]"/></value>
                </xsl:for-each>
            </values>
        </xsl:variable>

        <xsl:value-of select="sum($values//value)"/>

    </xsl:template>

</xsl:stylesheet>

对于问题中提供的输入数据,应用的结果只是它们的总和:

 15

1
这不是一个“XSLT 1.0”的解决方案——永远不要根据一个所谓的XSLT 1.0转换在XSLT 2.0处理器上成功运行这一事实得出任何结论。请尝试使用Saxon 6.5.x、MSXMLx、.NET XslCompiledTransform或任何其他符合XSLT 1.0标准的XSLT处理器进行测试。然后修正你的错误,或者更好地想出一个解决方案。请注意:我没有对你进行贬低评价。我相信我们在SO中的参与是一个学习的过程,希望看到每个人都能成长。 - Dimitre Novatchev
@Dimitre:我曾认为Saxon能够根据其规定的版本处理xsl。现在我明白了关于版本1.0警告的原因。然后我尝试了一下使用Saxon 6解决方案,但失败了。如果我没记错,在XSLT 1.0中不能像那样对节点集使用xsl:value-of。感谢您的评论。 - Emiliano Poggi

-1
尝试这段C#代码:
 var studenst = XElement.Load("studentRecord.xml");
 var marks= XElement.Load("marks.xml");

 Dictionary<string, int> marksDic = new Dictionary<string, int>();
 foreach (XElement m in marks.Descendants())
      {
           if (m.Attribute("type") != null)
                marksDic.Add(m.Attribute("type").Value, int.Parse(m.Value));
      }


      foreach (XElement s in studenst.Descendants().Where(x=>(x.Attribute("id") !=null ?int.Parse(x.Attribute("id").Value):0)==id))
      {

           Console.WriteLine(marksDic.Where(x => x.Key == s.Value)
                .Select(x => x.Value).Single());
      }
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接