0

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

<?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>
4

1 回答 1

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 的标签开头并以关于拒绝收件人地址的样板文件结尾的行时,我们将出现在前缀之后和后缀之前的子字符串,并将其包装在一个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>

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

        <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>

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

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

<?xml version="1.0"?>
<root><EMAILID>abcdef@gmail.com</EMAILID>
<EMAILID>abcdefgh@gmail.com</EMAILID>
<EMAILID>12345678@gmail.com</EMAILID>
<EMAILID>12345678@gmail.com</EMAILID>
<EMAILID>abcdefgh@gmail.com</EMAILID>
<EMAILID>12345678@gmail.com</EMAILID>
<EMAILID>12345678@gmail.com</EMAILID>
</root> 

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

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

于 2013-02-16T19:27:09.923 回答