使用Java获取SOAP消息中的特定部分

2025/04/07

1. 概述

在设计用于数据交换的API时,我们经常采用REST或SOAP架构方法。在使用SOAP协议的情况下,有时我们需要从SOAP消息中提取一些特定数据以进行进一步处理。

在本教程中,我们将学习如何在Java中获取SOAP消息的特定部分。

2. SOAPMessage类

在深入研究之前,让我们简单检查一下SOAPMessage类的结构,该类是所有SOAP消息的根类:

该类由两个主要部分组成-SOAP部分和可选附件部分。前者包含SOAP信封,其中包含我们收到的实际消息。此外,信封本身由标头和正文元素组成。

从Java 11开始,Java EE(包括JAX-WS和SAAJ模块)已从JDK中删除,不再是标准发行版的一部分。要使用Jakarta EE 9及更高版本成功处理SOAP消息,我们需要在pom.xml中添加Jakarta SOAP with Attachment APIJakarta SOAP Implementation依赖:

<dependency>
    <groupId>jakarta.xml.soap</groupId>
    <artifactId>jakarta.xml.soap-api</artifactId>
    <version>3.0.1</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.messaging.saaj</groupId>
    <artifactId>saaj-impl</artifactId>
    <version>3.0.3</version>
</dependency>

3. 示例

接下来,让我们创建一个将在本教程中使用的XML消息

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:be="http://www.tuyucheng.com/soap/">
    <soapenv:Header>
        <be:Username>tuyucheng</be:Username>
    </soapenv:Header>
    <soapenv:Body>
        <be:ArticleRequest>
            <be:Article>
                <be:Name>Working with JUnit</be:Name>
            </be:Article>
        </be:ArticleRequest>
    </soapenv:Body>
</soapenv:Envelope>

4. 从SOAP消息中获取标头和正文

接下来,让我们看看如何从SOAP消息中提取标头和正文元素。

根据SOAPMessage类层次结构,要获取实际的SOAP消息,我们首先需要获取SOAP部分,然后获取信封

InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("soap-message.xml");
SOAPMessage soapMessage = MessageFactory.newInstance().createMessage(new MimeHeaders(), inputStream);
SOAPPart part = soapMessage.getSOAPPart();
SOAPEnvelope soapEnvelope = part.getEnvelope();

现在,要获取标头元素,我们可以调用getHeader()方法:

SOAPHeader soapHeader = soapEnvelope.getHeader();

类似地,我们可以通过调用getBody()方法提取body元素:

SOAPBody soapBody = soapEnvelope.getBody();

5. 从SOAP消息中获取特定元素

现在我们已经讨论了检索基本元素,让我们探索如何从SOAP消息中提取特定部分。

5.1 根据标签名获取元素

我们可以使用getElementsByTagName()方法来获取特定元素,该方法返回一个NodeList。此外,Node是所有DOM组件的主要数据类型。换句话说,所有元素、属性和文本内容都被视为Node类型。

我们从XML中提取Name元素:

@Test
void whenGetElementsByTagName_thenReturnCorrectBodyElement() throws Exception {
    SOAPEnvelope soapEnvelope = getSoapEnvelope();
    SOAPBody soapBody = soapEnvelope.getBody();
    NodeList nodes = soapBody.getElementsByTagName("be:Name");
    assertNotNull(nodes);

    Node node = nodes.item(0);
    assertNotNull(node);
    assertEquals("Working with JUnit", node.getTextContent());
}

这里需要注意的是,我们需要将命名空间前缀传递给方法才能让它起作用

同样,我们可以使用相同的方法从SOAP标头中获取元素:

@Test
void whenGetElementsByTagName_thenReturnCorrectHeaderElement() throws Exception {
    SOAPEnvelope soapEnvelope = getSoapEnvelope();
    SOAPHeader soapHeader = soapEnvelope.getHeader();
    NodeList nodes = soapHeader.getElementsByTagName("be:Username");
    assertNotNull(nodes);

    Node node = nodes.item(0);
    assertNotNull(node);
    assertEquals("tuyucheng", node.getTextContent());
}

5.2 迭代子节点

从特定元素获取值的另一种方法是遍历子节点。

让我们看看如何迭代body元素的子节点:

@Test
void whenGetElementUsingIterator_thenReturnCorrectBodyElement() throws Exception {
    SOAPEnvelope soapEnvelope = getSoapEnvelope();
    SOAPBody soapBody = soapEnvelope.getBody();
    NodeList childNodes = soapBody.getChildNodes();

    for (int i = 0; i < childNodes.getLength(); i++) {
        Node node = childNodes.item(i);
        if ("Name".equals(node.getLocalName())) {
            String name = node.getTextContent();
            assertEquals("Working with JUnit", name);
        }
    }
}

5.3 使用XPath

接下来我们来看看如何使用XPath来提取元素。简单来说,XPath是用来描述XML部分的语法。此外,它还与XPath表达式配合使用,我们可以在特定条件下使用它来检索元素

首先,让我们创建一个新的XPath实例:

XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xpath = xPathFactory.newXPath();

为了有效地处理命名空间,让我们定义命名空间上下文:

xpath.setNamespaceContext(new NamespaceContext() {
    @Override
    public String getNamespaceURI(String prefix) {
        if ("be".equals(prefix)) {
            return "http://www.tuyucheng.com/soap/";
        }
        return null;
    }

    // other methods
});

这样,XPath就知道在哪里寻找我们的数据。

接下来,让我们定义检索Name元素值的XPath表达式:

XPathExpression expression = xpath.compile("//be:Name/text()");

在这里,我们使用路径表达式和返回节点文本内容的text()函数的组合创建了XPath表达式。

最后,让我们调用valuate()方法来检索匹配表达式的结果:

String name = (String) expression.evaluate(soapBody, XPathConstants.STRING); 
assertEquals("Working with JUnit", name);

此外,我们可以创建一个忽略命名空间的表达式

@Test
void whenGetElementUsingXPathAndIgnoreNamespace_thenReturnCorrectResult() throws Exception {
    SOAPBody soapBody = getSoapBody();
    XPathFactory xPathFactory = XPathFactory.newInstance();
    XPath xpath = xPathFactory.newXPath();
    XPathExpression expression = xpath.compile("//*[local-name()='Name']/text()");

    String name = (String) expression.evaluate(soapBody, XPathConstants.STRING);
    assertEquals("Working with JUnit", name);
}

我们在表达式中使用了local-name()函数来忽略命名空间,因此,表达式选择任何具有局部名称Name的元素,而不考虑命名空间前缀。

6. 总结

在本文中,我们学习了如何从Java中的SOAP消息中获取特定部分。

总而言之,有多种方法可以从SOAP消息中检索某些元素。我们探索了多种方法,例如通过标签名称搜索元素、迭代子节点以及使用XPath表达式。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章