Reputation: 2263
I'm fairly new to SOAP and iOS, so please bear with me. I'm attempting to consume a SOAP WS using source SudzC generated.
Looking at the output, things appear to be almost working. I can see the data I need, so data seems to be moving across the wire:
2012-03-30 17:26:14.131 EZSystem[892:707]
Loading: http://xx.xx.xx.xxx:xxxx/service
2012-03-30 17:26:14.137 EZSystem[892:707]
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns="http://database.usah.com/">
<soap:Body>
<getQB2012CustomerDictionaryXML></getQB2012CustomerDictionaryXML>
</soap:Body>
</soap:Envelope>
2012-03-30 17:26:14.485 EZSystem[892:707]
<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getQB2012CustomerDictionaryXMLResponse
xmlns:ns2="http://xxxxxx/">
<return>
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!--
<[Quickbooks tag]>Database Column Name</[Quickbooks tag]>
--><CustomerRet database_root_tag="Customers" id="id_customers"
...
...
...
</return>
</ns2:getQB2012CustomerDictionaryXMLResponse>
</S:Body>
</S:Envelope>
I am sending a string that represents XML, or is XML.
However, the following code returns null:
-(void)logIn{
DictionaryQB2012DatabaseKeyService* service =
[DictionaryQB2012DatabaseKeyService service];
service.logging = YES;
// Returns NSString*.
[service getQB2012CustomerDictionaryXML:self
action:@selector(getQB2012CustomerDictionaryXMLHandler:)];
}
// Handle the response from getQB2012CustomerDictionaryXML.
- (void) getQB2012CustomerDictionaryXMLHandler: (id) value {
// Handle errors
if([value isKindOfClass:[NSError class]]) {
NSLog(@"%@", value);
return;
}
// Handle faults
if([value isKindOfClass:[SoapFault class]]) {
NSLog(@"%@", value);
return;
}
// Do something with the NSString* result
NSString* result = (NSString*)value;
NSLog(@"getQB2012CustomerDictionaryXML returned the value: %@", result);
}
The value is null.
Where in the process is it breaking down? It appears that I am receiving the data I need, but getting it to an OBJ-C string is failing.
Any help would be appreciated,
Dane
Upvotes: 1
Views: 1853
Reputation: 2263
In SudzC generated SoapRequest.m:
- (void) connectionDidFinishLoading:(NSURLConnection *) connection {
...
if(self.logging == YES){
NSString* response = [[NSString alloc] initWithData: self.receivedData
encoding: NSUTF8StringEncoding];
NSLog(@"%@", response);
}
...
}
The response is the raw SOAP response, and it contains the data as expected.
In the same method,
CXMLNode* element = [[Soap getNode: [doc rootElement] withName: @"Body"]
childAtIndex:0];
This line gets the return value from the SOAP response. After some poking around, it turns out "Body" was the problem:
The SOAP response has "S:Body", so changing withName to the proper tag name fixes everything.
This bothers me, as I don't know whether my WS is to fault or what, but I'll take it for now.
Also, the Soap.m may have to be modified, as it uses "soap" instead of, say, "soapenv". These values are hard coded, but the change is fairly easy.
Also, the namespace is left empty:
[s appendFormat:@"=\"http://schemas.xmlsoap.org/soap/envelope/\"
xmlns=\"%@\">", ns];
This may have to be xmlns:[namespace]=\"%@"... where namespace is the stripped namespace.
That is:
http://namespace/ => namespace
Also, you cannot receive a string result and use it as in the examples, at least on my end. For example:
NSString* result = (NSString*) value;
should produce null. However this seems to work:
NSDictionary* result = (NSDictionary*)value;
NSString* finalResult = [[result allValues] objectAtIndex:0];
If anyone is interested, here are the modifications to the Soap.m I made.
// Soap.m
NSString* const SOAP_PREFIX = @"soapenv";
NSString* const HTTP_PREFIX = @"http://";
NSUInteger const FORWARD_FLASH_CHARACTER_VALUE = 47;
// Creates the XML request for the SOAP envelope with optional SOAP headers.
+ (NSString*) createEnvelope: (NSString*) method forNamespace: (NSString*) ns
forParameters: (NSString*) params withHeaders: (NSDictionary*) headers
{
NSMutableString* s = [NSMutableString string];
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<"];
[s appendString: SOAP_PREFIX];
[s appendString:@":Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-
instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""];
[s appendString:@" xmlns:"];
[s appendString: SOAP_PREFIX];
NSString* rawNamespace = [Soap getRawNamespace:ns];
[s appendFormat:@"=\"http://schemas.xmlsoap.org/soap/envelope/\"
xmlns:%@=\"%@\">", rawNamespace, ns];
if(headers != nil && headers.count > 0) {
[s appendString: @"<"];
[s appendString: SOAP_PREFIX];
[s appendString:@":Header>"];
for(id key in [headers allKeys]) {
if([[headers objectForKey: key] isMemberOfClass: [SoapNil class]])
{
[s appendFormat: @"<%@ xsi:nil=\"true\"/>", key];
} else {
[s appendString:[Soap serializeHeader:headers forKey:key]];
}
}
[s appendString: @"</"];
[s appendString: SOAP_PREFIX];
[s appendString:@":Header>"];
}
[s appendString: @"<"];
[s appendString: SOAP_PREFIX];
[s appendString:@":Body>"];
NSMutableString* fullMethodName = [NSMutableString string];
[fullMethodName appendString:rawNamespace];
[fullMethodName appendString:@":"];
[fullMethodName appendString:method];
[s appendFormat: @"<%@>%@</%@>", fullMethodName,[params
stringByReplacingOccurrencesOfString:@"&" withString:@"&"],
fullMethodName];
[s appendString: @"</"];
[s appendString: SOAP_PREFIX];
[s appendString:@":Body>"];
[s appendString: @"</"];
[s appendString: SOAP_PREFIX];
[s appendString:@":Envelope>"];
return s;
}
+(NSString*)getRawNamespace:(NSString *)value{
if([value hasPrefix:HTTP_PREFIX]){
NSString* rawNamespace = [value substringFromIndex:([HTTP_PREFIX length])];
if([rawNamespace length] == 0){
return @"";
}
// strip out a trailing slash
if([rawNamespace characterAtIndex:([rawNamespace length]- 1)] ==
FORWARD_FLASH_CHARACTER_VALUE){
NSRange range = NSMakeRange (0, [rawNamespace length] - 1);
rawNamespace = [rawNamespace substringWithRange:(range)];
}
return rawNamespace;
}
else{
return value;
}
}
Upvotes: 1