Get Specific Part From SOAP Message in Java – 用 Java 从 SOAP 消息中获取特定部分

最后修改: 2024年 1月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

We often employ REST or SOAP architectural approaches when designing API for data exchange. In the case of working with SOAP protocol, there may be situations when we need to extract some specific data from a SOAP message for further processing.

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

In this tutorial, we’ll learn how to get a specific part of a SOAP Message in Java.

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

2. The SOAPMessage Class

2.SOAPMessage

Before we dive in, let’s briefly examine the structure of the SOAPMessage class, which represents the root class for all SOAP messages:

在深入了解之前,让我们简要检查一下 SOAPMessage 类的结构,它代表了所有 SOAP 消息的根类:

Structure of SOAPMessage class.

The class consists of two main parts – the SOAP part and the optional attachment part. The former contains the SOAP Envelope, which holds the actual message we received. Additionally, the envelope itself is composed of the header and body elements.

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

From Java 11, Java EE, including the JAX-WS and SAAJ modules, were removed from the JDK and are no longer part of the standard distribution. To successfully process SOAP messages with Jakarta EE 9 and above, we’ll need to add the Jakarta SOAP with Attachment API and Jakarta SOAP Implementation dependencies in our pom.xml:

从 Java 11 开始,Java EE(包括 JAX-WS 和 SAAJ 模块)已从 JDK 中删除,不再是标准发布版本的一部分。要使用 Jakarta EE 9 及以上版本成功处理 SOAP 消息,我们需要添加 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. Working Example

3.工作实例

Next, let’s create an XML message we’ll use through this tutorial:

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

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:be="http://www.baeldung.com/soap/">
    <soapenv:Header>
        <be:Username>baeldung</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. Get Header and Body From SOAP Message

4.从 SOAP 报文中获取报头和正文

Moving forward, let’s see how to extract header and body elements from the SOAP message.

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

According to the SOAPMessage class hierarchy, to obtain the actual SOAP message, we’ll first need to get the SOAP part and then the envelope:

根据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();

Now, to get the header element, we can call the getHeader() method:

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

SOAPHeader soapHeader = soapEnvelope.getHeader();

Similarly, we can extract the body element by calling the getBody() method:

同样,我们可以通过调用 getBody() 方法来提取正文元素:

SOAPBody soapBody = soapEnvelope.getBody();

5. Get Specific Element From SOAP Message

5.从 SOAP 报文中获取特定元素

Now that we’ve discussed retrieving the basic elements, let’s explore how to extract specific parts from the SOAP message.

既然我们已经讨论了基本元素的检索,下面就让我们来探讨如何从 SOAP 消息中提取特定部分。

5.1. Get Elements by Tag Name

5.1 通过标签名称获取元素

We can use the getElementsByTagName() method to get a specific element. The method returns a NodeList. Moreover, the Node is the primary data type for all DOM components. In other words, all the elements, attributes, and text contents are considered to be of the Node type.

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

Let’s extract the Name element from the XML:

让我们从 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());
}

It’s important to note here that we need to pass the namespace prefix to the method for it to work correctly.

这里需要注意的是,我们需要将命名空间前缀传递给该方法,以使其正常工作。

Likewise, we can use the same approach to get an element from the SOAP header:

同样,我们也可以使用同样的方法从 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("baeldung", node.getTextContent());
}

5.2. Iterate Over Child Nodes

5.2.遍历子节点

Another way to get the value from a particular element is by iterating over child nodes.

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

Let’s see how to iterate over child nodes of a body element:

让我们看看如何遍历 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. Using XPath

5.3.使用 XPath

Next, let’s see how to extract elements using the XPath. Simply put, XPath is the syntax used to describe XML parts. Furthermore, it works with the XPath expressions, which we can use to retrieve elements under certain conditions.

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

First, let’s create a new XPath instance:

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

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

To effectively handle namespaces, let’s define the namespace context:

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

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

    // other methods
});

This way, XPath knows where to look for our data.

这样,XPath 就知道该去哪里找我们的数据了。

Next, let’s define the XPath expression that retrieves the value of the Name element:

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

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

Here, we created the XPath expression using a combination of the path expression and the text() function that returns the node’s text content.

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

Lastly, let’s call the evaluate() method to retrieve the result of the matching expression:

最后,让我们调用 evaluate() 方法来获取匹配表达式的结果:

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

Additionally, we can create an expression that ignores the namespaces:

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

@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);
}

We used the local-name() function in the expression to ignore namespaces. Therefore, the expression selects any element with the local name Name without considering the namespace prefix.

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

6. Conclusion

6.结论

In this article, we learned how to get a specific part from a SOAP message in Java.

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

To sum up, there are various ways to retrieve certain elements from the SOAP message. We explored methods such as searching for an element by its tag name, iterating over child nodes, and using XPath expressions.

总之,有多种方法可以从 SOAP 消息中检索某些元素。我们探讨了通过标签名称搜索元素、遍历子节点和使用 XPath 表达式等方法。

As always, the entire code examples are available over on GitHub.

与往常一样,整个代码示例可在 GitHub 上获取