需要使用XSLT从xml中获取特定值

时间:2013-02-16 18:00:17

标签: xml xslt

您好我有以下示例xml,我需要从xml文档中获取不同的无效电子邮件。 我想所有的时间项目都喜欢 “嵌套异常是:         com.sun.mail.smtp.SMTPAddressFailedException:550 5.1.1& lt“and        “:收件人地址被拒绝:虚拟别名表中的用户未知;”    是不变的

<?xml version = "1.0" encoding = "UTF-8"?>
<root>
    <Error_Message>Error sending mail message. Cause: javax.mail.SendFailedException: Invalid Addresses;
  nested exception is:
    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 &lt;abcdef@gmail.com>: Recipient address rejected: User unknown in virtual alias table
;
  nested exception is:
    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 &lt;abcdefgh@gmail.com>: Recipient address rejected: User unknown in virtual alias table
;
  nested exception is:
    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 &lt;12345678@gmail.com>: Recipient address rejected: User unknown in virtual alias table
;
  nested exception is:
    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 &lt;12345678@gmail.com>: Recipient address rejected: User unknown in virtual alias table
;
  nested exception is:
    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 &lt;abcdefgh@gmail.com>: Recipient address rejected: User unknown in virtual alias table
;
  nested exception is:
    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 &lt;12345678@gmail.com>: Recipient address rejected: User unknown in virtual alias table
;
  nested exception is:
    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 &lt;12345678@gmail.com>: Recipient address rejected: User unknown in virtual alias table
    </Error_Message>
    <err_mesage>5</err_mesage>
</root>

预期输出为:

<root>
<EMAILID>abcdef@gmail.com</EMAILID>
<EMAILID>abcdefgh@gmail.com@gmail.com</EMAILID>
<EMAILID>12345678@gmail.com</EMAILID>
</root>

1 个答案:

答案 0 :(得分:2)

正如Martin Honnen所说,analyze-string在这里是一个不错的选择。但是你的消息格式非常简单,你不需要比XSLT 1.0的简单字符串操作函数和递归命名模板更复杂的东西。这是一个带有嵌入式注释的XSLT 1.0样式表,用于解释正在发生的事情。

样式表的开头是完全传统的:

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

  <xsl:output method="xml" indent="yes"/>

我们为错误消息中的一些常量文本声明了两个变量(除了想要避免多次给出这些长常量字符串之外没有特别的原因):

  <xsl:variable name="prefix"
                select="'    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 &lt;'"/>
  <xsl:variable name="suffix"
                select="'>: Recipient address rejected: User unknown in virtual alias table'"/>

root元素复制自己:

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

Error_Message元素将其字符串值移交给命名模板extract-email-addresses,该模板的名称与其名称相同(详情请参阅下文)。

  <xsl:template match="Error_Message">
    <xsl:call-template 
        name="extract-email-addresses">
      <xsl:with-param name="s" 
                      select="string(.)"/>
    </xsl:call-template>
  </xsl:template>

err_mesage元素和文本节点被抑制:

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

extract-email-addresses模板接受字符串作为参数,默认为空字符串。

  <xsl:template name="extract-email-addresses">
    <xsl:param name="s" select="''"/>

我们要一次咬掉一些字符串s,处理我们被咬掉的部分,然后重复其余部分。所以我们要做的第一件事就是检查一下我们是否完成了。如果$s是空字符串,则没有什么可做的;我们停止递归并允许堆栈弹出。

    <xsl:choose>
      <xsl:when test="$s = ''">
        <!--* end of string, we are done. *-->
      </xsl:when>

当字符串不为空时,我们在第一个换行符上拆分字符串$s,将这两个部分分配给变量$s1$rest

      <xsl:otherwise>
        <xsl:variable name="s1" 
            select="substring-before($s,'&#xA;')"/>
        <xsl:variable name="rest" 
            select="substring-after($s,'&#xA;')"/>

现在我们寻找线路可以采用的各种形式。错误消息中的大多数行都是要忽略的样板:

        <xsl:choose>
          <xsl:when test="$s1 = 'Error sending mail message. Cause: javax.mail.SendFailedException: Invalid Addresses;'">
            <!--* this line is of no 
                * interest, continue *-->    
          </xsl:when>
          <xsl:when test="$s1 = '  nested exception is:'">
            <!--* skip this line *-->    
          </xsl:when>
          <xsl:when test="$s1 = ';'">
            <!--* skip this line *-->    
          </xsl:when>
          <xsl:when test="$s1 = ''">
            <!--* skip this line *-->    
          </xsl:when>

当我们看到以SMTPAddressFailedException的标签开头并以关于拒绝收件人地址的样板结尾的行时,我们采用在前缀之后和后缀之前发生的子字符串,并将其包装在{{1元素:

EMAILID

如果我们看到任何其他形式的行,那么输入不是预期的,所以我们发出一条诊断消息并继续:

          <xsl:when test="starts-with($s1,$prefix)
                          and
                          contains($s1,$suffix)">
            <EMAILID>
              <xsl:value-of select="
                substring-before(
                  substring-after($s1,$prefix),
                  $suffix)
                "/>
            </EMAILID>
            <xsl:text>&#xA;</xsl:text>
          </xsl:when>

无论我们在第一行做了什么,我们现在重复处理字符串中的其余行:

          <xsl:otherwise>
            <xsl:message>Unrecognized line: |<xsl:value-of
              select="$s1"/>|</xsl:message>
          </xsl:otherwise>
        </xsl:choose>

当然,XSLT 2.0分析字符串指令将比这更紧凑,并且XSLT 2.0的正则表达式使得复杂的事情比XSLT 1.0库更方便。 (但是如果你知道如何使用analyze-string,你就不会问你的问题.XSLT 1.0中较小的库和语言的一个优点是它有时比用1.0解决问题更快。它是要理解更复杂的XSLT 2.0结构以及如何将它们应用于一个简单的问题。当然,这是关于小语言和大语言的一般事实。)

应用于您显示的输入,刚刚列出的样式表几乎可以生成您显示的输出:

        <xsl:call-template name="extract-email-addresses">
          <xsl:with-param name="s" select="$rest"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

它不包括abcdefgh @ gmail.com @ gmail.com的一行;我猜想这可能是问题中的剪切/粘贴错误。

它也不检查是否已经发出给定行中的电子邮件地址;如果这在实践中是必不可少的,我希望你很明白如何传递包含迄今为止提取的所有电子邮件地址的第二个参数(由空格或U + A0或任何你喜欢的字符分隔,不能发生在一个电子邮件地址)并在发出EMAILID元素之前用它来测试重复项。