我正在开发一个测试用例,该用例断言 Wiremock 存根的 xml 请求正文没有
xmlns
属性。
到目前为止,我已经尝试使用 equalToXml
匹配器,但据我所知,这是忽略属性。
String customerXml = "<customer xmlns=\"http://example.com\"><name>Jon Doe</name><age>39</age></customer>";
WireMock.stubFor(
WireMock.get(urlEqualTo("/someservice/customers/123"))
.willReturn(
WireMock.aResponse()
.withStatus(HttpStatus.OK.value())
.withBody(customerXml)));
Customer customer = customersService.getCustomer(123);
WireMock.stubFor(
WireMock.post(urlEqualTo(
"/otherservice/customers/123"))
.willReturn(
WireMock.aResponse()
.withStatus(HttpStatus.CREATED.value())));
Customer customerInSecondSystem = customersService.createCustomer(customer);
String expectedRequestBody = "<customer><name>Jon Doe</name><age>39</age></customer>";
verify(WireMock.postRequestedFor(urlEqualTo("/otherservice/customers/123"))
.withRequestBody(equalToXml(expectedRequestBody))); // this will pass even though
// the xmlns attribute is present in the xml
然后我尝试想出一个可以使用的 XPath 表达式:
String expectedRequestBody = "<customer><name>Jon Doe</name><age>39</age></customer>";
verify(WireMock.postRequestedFor(urlEqualTo("/otherservice/customers/123"))
.withRequestBody(equalToXml(expectedRequestBody))
.withRequestBody(matchingXPath("*[@xmlns='http://example.com']")));
这将积极匹配 xml 中的
xmlns
属性。
现在,我必须如何制定 XPath 来否定匹配xmlns
属性(以确保它不存在)?
虽然命名空间声明具有属性的语法形式,但它们在 XPath 1.0 数据模型中不被视为属性。所以实际上对于以下 XML 文档,
<customer xmlns="http://example.com"/>
...以下 XPath 表达式不应返回任何内容:
/customer/@xmlns
相反,名称空间声明在 XPath 的数据模型中显示为“名称空间节点”。每个命名空间节点都有一个前缀(可以为空)和一个 URI。
如果命名空间声明的形式为
xmlns="http://example.com/"
,那么这将设置一个所谓的“默认命名空间”,其命名空间前缀为空。如果声明的形式为 xmlns:foo="http://example.com/"
,则命名空间前缀为“foo”,但在这两种情况下,命名空间 URI(实际上是声明的重要部分)为 http://example.com/
。
对于给定元素,您可以使用
namespace-uri()
函数访问其命名空间 URI;使用 local-name()
函数计算其名称不带前缀,使用
name()
函数计算其名称包括任何前缀。
注意,在下面的两个文档中,根元素的名称被认为是相同的(!)。 XML 元素的名称是由两个值组成的 pair:“本地”部分(在这两种情况下都是
customer
)和名称空间 URI(在这两种情况下都是 http://example.com/
)。命名空间前缀(分别为“”和“foo
”)并不重要。
<foo:customer xmlns:foo="http://example.com/"/>
<customer xmlns="http://example.com/"/>
您可以使用
namespace
轴访问 XPath 中的命名空间节点。例如以下表达式将返回根 customer
元素范围内的命名空间节点:
/customer/namespace::node()
您可以在命名空间节点上使用
local-name()
函数来读取其前缀,并使用 string()
函数来读取其命名空间 URI。
从你的问题中不清楚你真正打算施加什么限制。从表面上看,听起来您想要检查根元素上是否没有默认命名空间的声明,但我猜测您可能想要的是检查根元素的命名空间 URI 是否为空(即根元素的名称不属于命名空间):
namespace-uri(/*) = ''
如果事实上您确实想检查根元素是否没有声明“默认”命名空间,那么您可以使用这个布尔 XPath 表达式:
not(/*/namespace::node()[local-name(.)=''])
(意思是“根元素不有一个名称空间前缀为空的名称空间节点”)。