node-soap adding namespace to envelope - soap

I am trying to consume this soap service: http://testws.truckstop.com:8080/v13/Posting/LoadPosting.svc?singleWsdl with node-soap, but the client is mangling the namespaces and I have been unable to find a working solution.
I believe the answer is to either add a namespace to the soap envelope, or overwrite the soap envelope.
Using Soap UI, the request should look like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:v11="http://webservices.truckstop.com/v11"
xmlns:web="http://schemas.datacontract.org/2004/07/WebServices">
<soapenv:Header/>
<soapenv:Body>
<v11:GetLoads>
<v11:listRequest>
<web:IntegrationId>integrationId</web:IntegrationId>
<web:Password>password</web:Password>
<web:UserName>username</web:UserName>
</v11:listRequest>
</v11:GetLoads>
</soapenv:Body>
</soapenv:Envelope>
However, when I do:
client = soap.createClient(url);
let query = {
listRequest: {
Password: password,
UserName: username,
IntegrationId: integrationId
}
};
let results = client.GetLoads(query);
The client generates this xml:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:tns="http://webservices.truckstop.com/v11"
xmlns:q1="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q2="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q3="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q4="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q5="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q6="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q7="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q8="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q9="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q10="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q11="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q12="http://schemas.datacontract.org/2004/07/WebServices.Objects">
<soap:Body>
<GetLoads xmlns="http://webservices.truckstop.com/v11">
<listRequest>
<ns1:IntegrationId>integrationId</ns1:IntegrationId>
<ns1:Password>password</ns1:Password>
<ns1:UserName>usernam</ns1:UserName>
</listRequest>
</GetLoads>
</soap:Body>
</soap:Envelope>
This fails because IntegrationId, Password and UserName need http://schemas.datacontract.org/2004/07/WebServices, but the namespace isn't referenced in the envelope.
I've tried updating the client to add the namespace as suggested here:
client.wsdl.definitions.xmlns.ns1 = "http://schemas.datacontract.org/2004/07/WebServices";
client.wsdl.xmlnInEnvelope = client.wsdl._xmlnsMap();
I can see the namespace in client.wsdl.xmlnInEnvelope, but it doesn't seem to change the actual generated xml.
Is there another step required to refresh the client to use the updated envelope?
I also tried overriding the root element as shown here:
var wsdlOptions = {
//namespaceArrayElements: "xmlns:ns1=http://schemas.datacontract.org/2004/07/WebServices"
"overrideRootElement": {
"namespace": "xmlns:tns",
"xmlnsAttributes": [{
"name": "xmlns:tns",
"value": "http://webservices.truckstop.com/v11"
}, {
"name": "xmlns:ns1",
"value": "http://schemas.datacontract.org/2004/07/WebServices"
}]
}
};
this.loadPostClient = soap.createClient(this.tsConfig.loadPostUrl, wsdlOptions);
This changes the root body element:
<soap:Body>
<xmlns:tns:GetLoads
xmlns:tns="http://webservices.truckstop.com/v11"
xmlns:ns1="http://schemas.datacontract.org/2004/07/WebServices">
<listRequest>
<ns1:IntegrationId>integrationId</ns1:IntegrationId>
<ns1:Password>password</ns1:Password>
<ns1:UserName>username</ns1:UserName>
</listRequest>
</xmlns:tns:GetLoads>
</soap:Body>
But the remote server doesn't understand.
Thank you for reading!

This answer was correct all along
It wasn't working for me due to autocomplete and similar fields
client.wsdl.xmlnInEnvelope = client.wsdl._xmlnsMap();
Should have been:
client.wsdl.xmlnsInEnvelope = client.wsdl._xmlnsMap();
I left out an s and was setting xmlnInEnvelope instead of xmlnsInEvelope

It's been a few years, but I ran into a similar need of adding custom attributes to the soap envelope and wanted to give an alternative.
As of this writing, that _xmlnsMap() is a private method on the WSDL class so you can use it at your own risk. I always take private methods as subject to change from the developer without any notice to the library consumers so I wanted to find another way and turns out its possible.
TL;DR - Create your own WSDL class instance and pass it to your own Client class instance.
Use the open_wsdl method to bring in your WSDL
Use the callback to build your own custom attributes in a concatenated string.
Assign the attributes to the public xmlnsInEnvelope property.
return the updated WSDL instance (I used a promise).
const fetchWSDL = new Promise<WSDL>((resolve, reject) => {
// method that returns a WSDL instance from a url/file path
open_wsdl(this.wsdl, (err: any, wsdl?: WSDL) => {
// Build custom attributes
if (wsdl && wsdl.definitions.xmlns) {
const xmlns: { [key: string]: string } = {
[your namespaces]: 'values',
};
// turn your custom attributes map into a single concatenated string
let str = '';
for (const alias in xmlns) {
const ns = xmlns[alias];
str += ' xmlns:' + alias + '="' + ns + '"';
}
// Leverage public attribute on WSDL instance to apply our custom attributes
wsdl.xmlnsInEnvelope = str;
resolve(wsdl);
}
reject(err);
});
});
Use the updated WSDL instance to create your own client.
NOTE: the createClient method is just a convenience wrapper for creating a WSDL instance and returning a new Client instance.
const ModifiedWSDL = await fetchWSDL;
// Create client with our modified WSDL instance
this.client = new Client(ModifiedWSDL)
// adjust your Client instance as needed
A bit more code that the OP, but hopefully more in line with node-soap types and safer to use if you plan to upgrade.

Related

How to evaluate SOAP response using Schematron?

I have a SOAP response below
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:getCountryResponse xmlns:ns2="http://spring.io/guides/gs-producing-web-service">
<ns2:country>
<ns2:name>Spain</ns2:name>
<ns2:population>46704314</ns2:population>
<ns2:capital>Madrid</ns2:capital>
<ns2:currency>EUR</ns2:currency>
</ns2:country>
</ns2:getCountryResponse>
</SOAP-ENV:Body>
and a Schematron file which I expect to spit out an error when I run the validation since the Header element does not have an attribute named 'foo'
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron">
<sch:ns uri="http://www.w3.org/2003/05/soap-envelope" prefix="SOAP-ENV"/>
<sch:pattern id="structure">
<sch:rule context="SOAP-ENV:Header">
<sch:assert test="#foo">The element Header must have an attribute named foo.</sch:assert>
</sch:rule>
</sch:pattern>
But I get no failures. What am I getting wrong? The code that I use for validation is
internal class SchematronValidatorUtil {
companion object {
#JvmStatic
fun isXmlStringValidAgainstSchema(schema: String, xml: String,
charset: Charset = StandardCharsets.UTF_8): SchematronResult {
val schematronResource = SchematronResourcePure.fromString(schema, charset)
val input = StreamSource(ByteArrayInputStream(xml.toByteArray(charset)))
val schematronOutput = schematronResource.applySchematronValidationToSVRL(input)
val failures = mutableListOf<String>()
return schematronOutput?.let { schemaOutput ->
schemaOutput.activePatternAndFiredRuleAndFailedAssert
.map { each -> each as? FailedAssert }
.forEach { each -> each?.let { value ->
failures.add(String.format("%s: %s", value.test, value.text)) } }
val type = if (failures.any())
SchematronResultType.XmlDoesNotMatchSchema else SchematronResultType.XmlMatchesSchema
return SchematronResult(type, failures)
} ?: SchematronResult(SchematronResultType.InvalidSchematronFile, failures)
}
}
}
The SOAP envelope declares the SOAP-ENV namespace prefix as http://schemas.xmlsoap.org/soap/envelope/
The Schematron schema declares the SOAP-ENV namespace prefix as http://www.w3.org/2003/05/soap-envelope
Change the ns declaration in the Schematron schema to match the namespace URI declared in the SOAP envelope, and it should work.
(A useful way to test an issue like this is to temporarily change the rule context to *:Header, which would match the Header element in any namespace. If the schema works with that change, then you've narrowed the problem down to a namespace issue.)

Spring integration: Using cusom header for http:outbound-channel-adapter

I have a situation to use a <int-http:outbound-channel-adapter ... /> to send an object with information stored in the header.
Following works when I call the <int-http:inbound-channel-adapter ... /> like follows:
public void openTicket(final Profile profile, final Ticket ticket) {
final HttpHeaders headers = new HttpHeaders();
headers.set("profile", profile.toString());
final HttpEntity<Ticket> entity = new HttpEntity<Ticket>(ticket, headers);
template.exchange(URL, HttpMethod.PUT, entity, Ticket.class);
}
This calls my inboung-channel-adapter successful with the given profile in the headers:
<int-http:inbound-channel-adapter
channel="api_app_integration_request_channel"
supported-methods="PUT"
path="/process/ticket"
request-payload-type="*.model.Ticket"
mapped-request-headers="profile"
error-channel="internal-client-rest-ticket-error-channel"
>
<int-http:request-mapping consumes="application/json" />
</int-http:inbound-channel-adapter>
What doesnt work is calling the service via outbound-channel-adapter, the call itself works, but my header 'profile' is gone.
<int-http:outbound-channel-adapter
channel="client_rest_ticket_outbound_channel"
http-method="PUT"
url="http://localhost:8080/process/ticket"
mapped-request-headers="profile"
/>
I am using Spring-Boot 1.3.6.RELEASE.
Custom headers are (currently) mapped with an X- prefix by default; to map them without the prefix you need to wire up a DefaultHttpHeaderMapper with userDefinedHeaderPrefix set to null (or "") as well as the outbound header name(s) you want to map.
See the documentation.
EDIT:
<bean class="org.springframework.integration.http.support.DefaultHttpHeaderMapper" id="headerMapper"
p:userDefinedHeaderPrefix=""
p:inboundHeaderNames="profile"
p:outboundHeaderNames="profile"
/>

Netsuite - how to specify record_type for getall request

I want to get a list of all InventoryItems
According to this document:
https://system.netsuite.com/help/helpcenter/en_US/Output/Help/SuiteCloudCustomizationScriptingWebServices/SuiteTalkWebServices/getAll.html
I'm forming the following request:
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:platformMsgs="urn:platform_2013_1.webservices.netsuite.com" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
<passport>
<email>******</email>
<password>******</password>
<account>******</account>
</passport>
</env:Header>
<env:Body>
<platformMsgs:getAll>
<recordType>InventoryItem</recordType>
</platformMsgs:getAll>
</env:Body>
</env:Envelope>
But receiving response with the error:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<platformMsgs:documentInfo xmlns:platformMsgs="urn:messages_2013_1.platform.webservices.netsuite.com">
<platformMsgs:nsId>WEBSERVICES_969904_100920131651936419141601801_cbf1690968b43</platformMsgs:nsId>
</platformMsgs:documentInfo>
</soapenv:Header>
<soapenv:Body>
<getAllResponse xmlns="urn:platform_2013_1.webservices.netsuite.com">
<platformCore:getAllResult xmlns:platformCore="urn:core_2013_1.platform.webservices.netsuite.com">
<platformCore:status isSuccess="false">
<platformCore:statusDetail type="ERROR">
<platformCore:code>GETALL_RCRD_TYPE_REQD</platformCore:code>
<platformCore:message>The getAll record type is required.</platformCore:message>
</platformCore:statusDetail>
</platformCore:status>
</platformCore:getAllResult>
</getAllResponse>
</soapenv:Body>
</soapenv:Envelope>
I've tried to request currencies, states - the response always the same
I've tried following variants:
<GetAllRecordType>inventoryItem</GetAllRecordType>
and
<recordType>inventoryItem</recordType>
and
<GetAllRecordType>currency</GetAllRecordType>
and
<recordType>currency</recordType>
with the same response:
<platformCore:message>The getAll record type is required.</platformCore:message>
According to https://webservices.netsuite.com/xsd/platform/v2013_2_0/coreTypes.xsd - I've specified correctly recordType (btw I've also tried without any success)
I'm using ruby and there is no complete library for ruby. The one that is exists doesn't contain almost all things I'm going to use.
Can someone help me what I'm doing wrong or may be someone have working example
getAll cannot be used for all record types. Here is what is supported via getAll:
budgetCategory
campaignAudience
campaignCategory
campaignChannel
campaignFamily
campaignOffer
campaignSearchEngine
campaignSubscription
campaignVertical
costCategory
currency
leadSource
salesTaxItem
state
supportCaseIssue
supportCaseOrigin
supportCasePriority
supportCaseStatus
supportCaseType
taxGroup
taxType
The most complete implementation of SuiteTalk in Ruby is the netsuite ruby gem.
It doesn't yet support ItemSearchAdvanced (note that unlike most NetSuite records there isn't a ItemSearchBasic). Also, note that there isn't a InventoryItemAdvancedSearch, NonInventoryItemAdvancedSearch, etc like you would expect. Take a look at the lists.accounting.xsd for more information in the schema browser.
Like someone else mentioned, there is no getAll call for item record types. The best (and possibly only) way to get all of the items in a NetSuite instance is do a paginate through a search and combine all of the results.
I've coded a quick hack to demonstrate what needs to be done in order to implement item searching in the NetSuite gem. I wouldn't use the referenced hack in production, but could you easily integrate item searching into the NetSuite gem using the info the above referenced hack.
Below is searching code that, when used in combination with the above hack, will get you all of the items in your NetSuite instance.
item_list = []
item_search = NetSuite::Records::InventoryItem.search(
criteria: {
basic: [
]
},
preferences: {
page_size: 100,
}
)
item_search.results_in_batches do |batch|
item_list += batch
end
(the above code will probably break if the search returns a NonInventoryItem or another item record that isn't a InventoryItem, but this should be enough to get you started)
I stumbled upon this question with the same issue as
<platformCore:message>The getAll record type is required.</platformCore:message>
Figured I would come post a snippet of what a proper soap envelope should look like for this request. As others have mentioned, this call only supports certain record types...but once you identify the record type you desire, the below code should help.
<soap:Envelope xmlns:platformFaults="urn:faults_2014_1.platform.webservices.netsuite.com" xmlns:platformMsgs="urn:messages_2014_1.platform.webservices.netsuite.com" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="urn:platform_2014_1.webservices.netsuite.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Header>
<passport>
<email>test#example.com</email>
<password>*******</password>
<account>AccountNumber</account>
<role internalId="3"/>
</passport>
</soap:Header>
<soap:Body>
<platformMsgs:getAll xmlns="urn:messages_2014_1.platform.webservices.netsuite.com" xmlns:platformMsgs="urn:messages_2014_1.platform.webservices.netsuite.com">
<platformMsgs:record recordType="currency"/>
</platformMsgs:getAll>
</soap:Body>
</soap:Envelope>
Also we are using the node-soap module to communicate with this web service and here is what it looks like from that perspective as well.
soap.createClient(this.url, function (err, client) {
client.addSoapHeader({
passport: {
email: 'test#example.com',
password: 'pass',
account: 'Acct Number',
role: {
attributes: { internalId: 3 }
}
}
});
client.getAll({
"record": {
"attributes": {
"recordType": "currency"
}
}
}, callback);
});
});
Anyways I hope this helps others as it did stump me for awhile.
inventoryItem, not InventoryItem.
Also, what language are you using? If PHP, there is a PHPToolkit to help you out.
As Dave suggested, the request will be case-sensitive, so make sure the value you use matches exactly what the XSD says.
If you scroll down the XSD further, you will see the enumeration for GetAllRecordType instead of just RecordType. This does not have an entry for inventoryItem or anything similar, so Inventory Items are most likely not available in this type of request.
You may instead have to build an Item search with no filters to return all Inventory Items.
For anyone using PHP and specially this library you can specify the record type this way:
$getAllRequest = new GetAllRequest();
$getAllRequest->record = new GetAllRecord();
$getAllRequest->record->recordType = RecordType::currency;
$getAllResponse = $service->getAll($getAllRequest);
This for example returns list of all the existing currencies.
Maybe this link is helpful : http://tellsaqib.github.io/NSPHP-Doc/df/d09/class_get_all_record_type.html
GetAllRecordType Class don't have InventoryItem
Data Fields
const budgetCategory = "budgetCategory"
const campaignAudience = "campaignAudience"
const campaignCategory = "campaignCategory"
const campaignChannel = "campaignChannel"
const campaignFamily = "campaignFamily"
const campaignOffer = "campaignOffer"
const campaignSearchEngine = "campaignSearchEngine"
const campaignSubscription = "campaignSubscription"
const campaignVertical = "campaignVertical"
const costCategory = "costCategory"
const currency = "currency"
const leadSource = "leadSource"
const salesTaxItem = "salesTaxItem"
const state = "state"
const supportCaseIssue = "supportCaseIssue"
const supportCaseOrigin = "supportCaseOrigin"
const supportCasePriority = "supportCasePriority"
const supportCaseStatus = "supportCaseStatus"
const supportCaseType = "supportCaseType"
const taxGroup = "taxGroup"
const taxType = "taxType"

How do I extract the ACK from a webservice response in Mirth?

I have a destination which sends an HL7 v2 message to a WCF webservice. I've managed to get the message correctly to the webservice (after fixing various encoding problems) and I can confirm that the message is reaching the WCF endpoint correctly. However, after much hunting around in the forums and documentation, I am unable to correctly parse the ACK we receive back to indicate when an error has occurred.
The response coming back from the webservice looks like:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header/>
<s:Body>
<ProcessMessageResponse xmlns="http://www.bluewire-technologies.com/webservices">
<ProcessMessageResult>
MSH|^~\&|Epro|RGR|||||ACK||D||||||GBR|ASCII|
MSA|AE||Empty message|
ERR|^^^100|
</ProcessMessageResult>
</ProcessMessageResponse>
</s:Body>
</s:Envelope>
The response contains an ACK in the ProcessMessageResult element. How to I extract this ACK from the response and pass it as the output of the destination? Will Mirth automatically parse the ACK and determine that there was an error?
I had thought that I needed some kind of transformer on the destination (called 'SOAP') and use something along the lines of:
var xml = responseMap.get('SOAP').getMessage();
and then extract the ProcessMessageResponse element but responseMap.get('SOAP') returns null so that seems the wrong approach.
I've now solved part of this thanks to an answer on the Mirth forums.
To summarize, I use the following postprocessor to extract the ack and update the status:
var s = new Namespace('http://schemas.xmlsoap.org/soap/envelope/');
var bw = new Namespace('http://www.bluewire-technologies.com/webservices');
var response = new XML($r('SOAP').getMessage());
var ack = new XML(SerializerFactory.getHL7Serializer().toXML(response.s::Body.bw::ProcessMessageResponse.bw::ProcessMessageResult.toString()));
var ackCode = ack.MSA['MSA.1']['MSA.1.1'].toString();
if(ackCode == 'AE')
{
var errorMessage = ack.MSA['MSA.3']['MSA.3.1'].toString();
var messageController = com.mirth.connect.server.controllers.DefaultMessageObjectController.create();
var channelStatisticsController = com.mirth.connect.server.controllers.DefaultChannelStatisticsController.create();
messageObject.getContext().put("replace", "true"); // yuk - this is to make setError below work.
messageController.setError(messageObject, null, errorMessage, null, null);
channelStatisticsController.decrementSentCount(new java.lang.String(channelId));
}
Not pretty, but it works...

Groovy HTTPBuilder SOAP response not being parsed properly

I don't understand why XmlSlurper is apparently not working on the result.
import groovyx.net.http.*
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*
def String WSDL_URL = ...
def http = new HTTPBuilder( WSDL_URL , ContentType.XML )
String soapEnvelope =
"""<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetTerritories xmlns="...">
<State>AZ</State>
<ZipCode>85203</ZipCode>
</GetTerritories>
</soap12:Body>
</soap12:Envelope>"""
http.request( POST, XML ) {
headers."Content-Type" = "application/soap+xml; charset=utf-8"
headers."Accept" = "application/soap+xml; charset=utf-8"
body = soapEnvelope
response.success = { resp, xml ->
println "XML was ${xml}"
println "Territories were ${xml.Territories}"
println "State were ${xml.Territories.State}"
println "City was ${xml.Territories.Territory.City}"
println "County was ${xml.Territories.Territory.County}"
}
response.failure = { resp, xml ->
xml
}
}
leads to
XML was <Territories><State>AZ</State><ZipCode>85203</ZipCode><Territory><City>Mesa</City><County>Maricopa</County>...</Territory></Territories>
Territories were
State were
City was
County was
UPDATE: Thanks to John Wagenleitner's insight, I did a little more digging.
When I add that assert, I see an issue:
assert "Territories" == xml.name()
| | |
| | Envelope
| <Territories><State>AZ</State><ZipCode>85203</ZipCode</Territories>
false
Changing the request parameters from POST, XML to POST, TEXT is revealing:
XML was <?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetTerritoriesResponse xmlns="...">
<GetTerritoriesResult><Territories><State>AZ</State><ZipCode>85203</ZipCode><Territory><City>Mesa</City><County>Maricopa</County>...</Territory></Territories>
</GetTerritoriesResult>
</GetTerritoriesResponse>
</soap:Body>
</soap:Envelope>
...
So it looks like the XmlSlurper, when the variable is being printed out, is throwing away the SOAP stuff and evaluating the innermost node () while not actually navigating to that node. Is this expected behavior?
I have been unable to find a a more complete and modern SOAP call and parse using httpBuilder, so I assumed XML would be the right content type. But it looks like I'll just have to accept TEXT and parse the body myself, which seems lame. Is there a better way of handling SOAP responses with httpBuilder?
I would recommend printing the raw text of the response:
println "XML was ${resp.data.text}"
Assuming that the printed XML line is what you expect (though odd since there is no Envelope or Body nodes), then you should be able to remove Territories from your references to xml. When parsed with XmlSlurper the root node is the GPathResult.
assert "Territories" == xml.name()
println "State were ${xml.State.text()}"
println "City were ${xml.Territory.City.text()}"
println "County were ${xml.Territory.County.text()}"
Also, just wanted to point out that the SOAP 1.2 media type is "application/soap+xml".
UPDATE:
So it looks like the XmlSlurper, when the variable is being printed
out, is throwing away the SOAP stuff and evaluating the innermost node
() while not actually navigating to that node. Is this expected
behavior?
Yes, the toString() method for a GPathResult just prints all text nodes and not the actual elements or attributes. With HTTPBuilder you can print out the raw response text by using:
println resp.data.text
I have been unable to find a a more complete and modern SOAP call and
parse using httpBuilder, so I assumed XML would be the right content
type. But it looks like I'll just have to accept TEXT and parse the
body myself, which seems lame. Is there a better way of handling SOAP
responses with httpBuilder?
The ContentType.XML is fine, the issue is with how the SOAP response that your web service returns is formed. The web service is sending back the Territories results as an encoded string in the GetTerritoriesResult element and not as part of the actual XML response that HTTPBuilder automatically parses for you (this is not a problem with the way HTTPBuilder is handling it). Because the data you really want is in that encoded string you will need to parse the text node of the GetTerritoriesResult yourself.
response.success = { resp, xml ->
println "XML was ${resp.data.text}"
def territories = new XmlSlurper().parseText(
xml.Body.GetTerritoriesResponse.GetTerritoriesResult.text()
)
println "State were ${territories.State}"
println "City was ${territories.Territory.City}"
println "County was ${territories.Territory.County}"
}