[[contract-dsl-xml]]
== XML Support for HTTP

include::partial$_attributes.adoc[]

For HTTP contracts, we also support using XML in the request and response body.
The XML body has to be passed within the `body` element
as a `String` or `GString`. Also, body matchers can be provided for
both the request and the response. In place of the `jsonPath(...)` method, the `org.springframework.cloud.contract.spec.internal.BodyMatchers.xPath`
method should be used, with the desired `xPath` provided as the first argument
and the appropriate `MatchingType` as the second argument. All the body matchers apart from `byType()` are supported.

The following example shows a Groovy DSL contract with XML in the response body:

====
[source,groovy,indent=0,subs="verbatim",role="primary"]
.Groovy
----
include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/XmlMethodBodyBuilderSpec.groovy[tags=xmlgroovy]
----

[source,yml,indent=0,subs="verbatim",role="secondary"]
.YAML
----
include::{verifier_root_path}/src/test/resources/yml/contract_rest_xml.yml[indent=0]
----

[source,java,indent=0,subs="verbatim",role="secondary"]
.Java
----
include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_xml.java[tags=class,indent=0]
----

[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
include::{verifier_root_path}/src/test/resources/kotlin/contract_xml.kts[tags=class,indent=0]
----
====

The following example shows an automatically generated test for XML in the response body:

====
[source,java,indent=0]
----
@Test
public void validate_xmlMatches() throws Exception {
	// given:
	MockMvcRequestSpecification request = given()
				.header("Content-Type", "application/xml");

	// when:
	ResponseOptions response = given().spec(request).get("/get");

	// then:
	assertThat(response.statusCode()).isEqualTo(200);
	// and:
	DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder();
	Document parsedXml = documentBuilder.parse(new InputSource(
				new StringReader(response.getBody().asString())));
	// and:
	assertThat(valueFromXPath(parsedXml, "/test/list/elem/text()")).isEqualTo("abc");
	assertThat(valueFromXPath(parsedXml,"/test/list/elem[2]/text()")).isEqualTo("def");
	assertThat(valueFromXPath(parsedXml, "/test/duck/text()")).matches("[0-9]\{3}");
	assertThat(nodeFromXPath(parsedXml, "/test/duck/xxx")).isNull();
	assertThat(valueFromXPath(parsedXml, "/test/alpha/text()")).matches("[\\p\{L}]*");
	assertThat(valueFromXPath(parsedXml, "/test/*/complex/text()")).isEqualTo("foo");
	assertThat(valueFromXPath(parsedXml, "/test/duck/@type")).isEqualTo("xtype");
	}
----
====

[[xml-support-for-namespaces]]
=== XML Support for Namespaces
Namespaced XML is supported. However, any XPath expresssions used to select namespaced content must be updated.

Consider the following explicitly namespaced XML document:

[source,xml,indent=0]
----
<ns1:customer xmlns:ns1="http://demo.com/customer">
    <email>customer@test.com</email>
</ns1:customer>
----
The XPath expression to select the email address is: `/ns1:customer/email/text()`.

WARNING: Beware as the unqualified expression (`/customer/email/text()`) results in `""`.

For content that uses an unqualified namespace, the expression is more verbose. Consider the following XML document that
uses an unqualified namespace:

[source,xml,indent=0]
----
<customer xmlns="http://demo.com/customer">
    <email>customer@test.com</email>
</customer>
----
The XPath expression to select the email address is
```
*/[local-name()='customer' and namespace-uri()='http://demo.com/customer']/*[local-name()='email']/text()
```
WARNING: Beware, as the unqualified expressions (`/customer/email/text()` or `*/[local-name()='customer' and namespace-uri()='http://demo.com/customer']/email/text()`)
result in `""`. Even the child elements have to be referenced with the `local-name` syntax.

[[general-namespaced-node-expression-syntax]]
==== General Namespaced Node Expression Syntax
- Node using qualified namespace:
```
/<node-name>
```
- Node using and defining an unqualified namespace:
```
/*[local-name=()='<node-name>' and namespace-uri=()='<namespace-uri>']
```
NOTE: In some cases, you can omit the `namespace_uri` portion, but doing so may lead to ambiguity.

- Node using an unqualified namespace (one of its ancestor's defines the xmlns attribute):
```
/*[local-name=()='<node-name>']
```


