XSLT:如何使用 Muenchian 分组将 XML 转换为文本文件

Posted

技术标签:

【中文标题】XSLT:如何使用 Muenchian 分组将 XML 转换为文本文件【英文标题】:XSLT: How to convert XML into text file with Muenchian Grouping 【发布时间】:2011-05-24 20:56:31 【问题描述】:

我希望能再在这里得到帮助。这是我的示例输入 XML:

<Report>
  <RecordValues>
    <Record>
        <FieldValue fieldName="firm_name" fieldValue="Firm_1"/>
        <FieldValue fieldName="firm_number" fieldValue="11"/>
        <FieldValue fieldName="prepared_by" fieldValue="PARKER"/>
        <FieldValue fieldName="contact_number" fieldValue="123456789"/>
        <FieldValue fieldName="trade_date" fieldValue="2010-10-17"/>
        <FieldValue fieldName="symbol" fieldValue="ADM"/>
    </Record>
    <Record>
    <FieldValue fieldName="firm_name" fieldValue="Firm_1"/>
        <FieldValue fieldName="firm_number" fieldValue="11"/>
        <FieldValue fieldName="prepared_by" fieldValue="PARKER"/>
        <FieldValue fieldName="contact_number" fieldValue="123456789"/>
        <FieldValue fieldName="trade_date" fieldValue="2010-10-16"/>
        <FieldValue fieldName="symbol" fieldValue="ACW"/>
    </Record>
    <Record>
        <FieldValue fieldName="firm_name" fieldValue="Firm_2"/>
        <FieldValue fieldName="firm_number" fieldValue="12"/>
        <FieldValue fieldName="prepared_by" fieldValue="EDWARDS"/>
        <FieldValue fieldName="contact_number" fieldValue="123456780"/>
        <FieldValue fieldName="trade_date" fieldValue="2010-10-19"/>
        <FieldValue fieldName="symbol" fieldValue="ADS"/>
    </Record>
  </RecordValues>
</Report>

这是我需要得到的输出:

A Firm_1 11
B PARKER 123456789
C 2010-10-17 ADM
C 2010-10-16 ACW
T 4
A Firm_2 12
B EDWARDS 123456780
C 2010-10-19 ADS
T 3

如您所见,我需要按“公司名称”或“公司编号”对记录进行分组。每个组必须有一个“A”类型的记录、一个“B”类型的记录和多个“C”类型的记录。记录“T”是没有记录“T”的每个组的总和。输入 XML 已排序。我发现 Muenchian Method 正在对记录进行分组,但没有成功。显然我做错了什么。这是我写的 XSLT:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="text"/>
   <xsl:strip-space elements="*"/>

   <xsl:key name="value-by-firm" match="Report/RecordValues/Record/FieldValue" use="firm_number"/>
  <xsl:template match="Record">
   <xsl:for-each select="FieldValue/@fieldValue[count(. | key('value-by-firm', firm_number))]">
    <xsl:text>A </xsl:text>
    <xsl:value-of select="firm_name"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="firm_number"/>
    <xsl:text>&#xA;</xsl:text>
    <xsl:text>B </xsl:text>
    <xsl:value-of select="prepared_by"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="contact_number"/>
    <xsl:text>&#xA;</xsl:text>
    <xsl:for-each select="key('value-by-firm', firm_number)">
      <xsl:text>C </xsl:text>
      <xsl:value-of select="trade_date"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="symbol"/>
      <xsl:text>&#xA;</xsl:text>
   </xsl:for-each>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

也许还有另一种方法可以做到这一点。提前致谢。

【问题讨论】:

这没有任何意义!什么是“A 类记录”、“B 类记录”、“C 类记录”、“T 类记录”?总计有哪些值? @Dimitre Novatchev:这只是硬编码的记录标识符。 总数应该显示一个公司的记录数,而不是总数。 @klipa:在您的 XML 中,我没有看到任何这样的硬编码记录标识符。请编辑问题并使其有意义。 @klipa:请编辑您的问题并解释现在缺少的所有内容。此外,更正预期输出:T 的值必须是 2 和 1。 【参考方案1】:

这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:key name="kRecordByFirmAndContact" match="Record"
             use="concat(FieldValue[@fieldName='firm_number']
                            /@fieldValue,
                         '+',
                         FieldValue[@fieldName='contact_number']
                            /@fieldValue)"/>
    <xsl:template
         match="Record
                   [count(.|key('kRecordByFirmAndContact',
                                concat(FieldValue
                                          [@fieldName='firm_number']
                                          /@fieldValue,
                                       '+',
                                       FieldValue
                                          [@fieldName='contact_number']
                                          /@fieldValue))[1])
                    = 1 ]">
        <xsl:variable name="vRecords"
                      select="key('kRecordByFirmAndContact',
                                  concat(FieldValue
                                            [@fieldName='firm_number']
                                            /@fieldValue,
                                         '+',
                                         FieldValue
                                            [@fieldName='contact_number']
                                            /@fieldValue))"/>
        <xsl:value-of select="concat('A ',
                                     *[@fieldName='firm_name']
                                      /@fieldValue,
                                     ' ',
                                     *[@fieldName='firm_number']
                                      /@fieldValue,
                                     '&#xA;',
                                     'B ',
                                     *[@fieldName='prepared_by']
                                      /@fieldValue,
                                     ' ',
                                     *[@fieldName='contact_number']
                                      /@fieldValue,
                                     '&#xA;')"/>
        <xsl:apply-templates select="$vRecords" mode="RecordC"/>
        <xsl:value-of select="concat('T ',count($vRecords) + 2,'&#xA;')"/>
    </xsl:template>
    <xsl:template match="Record" mode="RecordC">
        <xsl:value-of select="concat('C ',
                                     *[@fieldName='trade_date']
                                      /@fieldValue,
                                     ' ',
                                     *[@fieldName='symbol']
                                      /@fieldValue,
                                     '&#xA;')"/>
    </xsl:template>
</xsl:stylesheet>

输出:

A Firm_1 11
B PARKER 123456789
C 2010-10-17 ADM
C 2010-10-16 ACW
T 4
A Firm_2 12
B EDWARDS 123456780
C 2010-10-19 ADS
T 3

注意:如您所见,这并不复杂,但您的架构使代码如此冗长......这看起来像用于数据转储的 M$ XML 格式。

【讨论】:

@Alejandro:非常感谢您的解决方案。我唯一不明白的为什么你要连接“firm_number”和“contact_number”? @klipa:因为这是关键,所以您按公司和“准备”它的人分组,看起来这个人的联系号码是唯一的。【参考方案2】:

您将“错误”的事物分组,并且错误地使用了这些组。

您正在尝试对 Record 元素进行分组。因此,这些应该与您的 xsl:key 匹配(use=... 属性应该引用公司名称) 分组技巧通过只处理每个组一次来起作用。您无法显式处理 xsl:key 中的每个键,因此您可以处理所有值,而忽略组中除了 first 值之外的所有值 - 并在那里进行整个组的处理。这意味着您的 foreach 应该选择与 xsl:key 相同的元素并添加节点测试 ala [count(. | reference-to-group[1]) = 1] - 在这里,您忘记了 [1]= 1 部分。

然后是固定的 XSLT 文件(请注意,字段查找也略有变化,我没有添加 T 计算:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="value-by-firm" match="/Report/RecordValues/Record" use="FieldValue[@fieldName='firm_number']/@fieldValue"/>
  <xsl:template match="/">
    <xsl:for-each select="/Report/RecordValues/Record[count(. | key('value-by-firm', FieldValue[@fieldName='firm_number']/@fieldValue)[1]) = 1]">
      <xsl:text>A </xsl:text>
      <xsl:value-of select="FieldValue[@fieldName='firm_name']/@fieldValue"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="FieldValue[@fieldName='firm_number']/@fieldValue"/>
      <xsl:text>&#xA;</xsl:text>
      <xsl:text>B </xsl:text>
      <xsl:value-of select="FieldValue[@fieldName='prepared_by']/@fieldValue"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="FieldValue[@fieldName='contact_number']/@fieldValue"/>
      <xsl:text>&#xA;</xsl:text>
      <xsl:for-each select="key('value-by-firm', FieldValue[@fieldName='firm_number']/@fieldValue)">
        <xsl:text>C </xsl:text>
        <xsl:value-of select="FieldValue[@fieldName='trade_date']/@fieldValue"/>
        <xsl:text> </xsl:text>
        <xsl:value-of select="FieldValue[@fieldName='symbol']/@fieldValue"/>
        <xsl:text>&#xA;</xsl:text>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

编辑:最后一点:鉴于此数据模式,您所做的并不是 XSLT 的强项之一。看起来您有一个可以自然地转换为更清晰的模式的数据结构(例如,其中名称-值对由 XML 的自然名称-值对表示;即属性)。或者,您可能希望将其导入“真正的”编程语言(几乎可以肯定这些数据的来源),其中不表示 FieldValue 元素以及 fieldName 和 fieldValue 属性等所有杂乱无章的东西。基本上;尽管使用 XML+XSLT 可以做到这一点,但您最终会得到一个更复杂、更脆弱的解决方案,而不是用更自然的表示形式来表示它并使用更自然的工具来处理它。

【讨论】:

感谢您的回复。我的 XSLT 知识非常有限。你能给我举个例子吗。谢谢。 哇,真是太快了。非常感谢。也许你也可以帮助我计算记录?

以上是关于XSLT:如何使用 Muenchian 分组将 XML 转换为文本文件的主要内容,如果未能解决你的问题,请参考以下文章

XSLT muenchian 在子节点中按值分组

如何使用 Muenchian 分组 XSLT 1.0 在每个目录中按标题分组

XSLT / Muenchian 分组:如何从组中选择具有某些子元素的元素?

XSLT 和 Muenchian 分组, 多级样本

Muenchian 分组 XSLT 1.0 多重分组

如何改进我的 Muenchian 分组 XSLT?