Objective-CでXML Webサービスを使ってみる

調べてもまともに情報が出てこずに、いろいろ大変だったのでメモ。
ここを使わせてもらう。

準備

wsdl2objcをダウンロードする。ここのUsageInstructionsはまるで役に立たないので無視。
先のサービスのWSDLを取得して*1、objcのソースを出力しておく。

実装

Xcodeを起動してプロジェクトを新規作成。


[TARGETS]-[Build Phases]にlibxml2.2.dylibを追加。

[TARGETS]-[Build Settings]-[Header Search Paths]に"${SDKROOT}/usr/include/libxml2"を追加。


先に出力したソースを全部プロジェクトに取り込む。


importして、

#import "TempConvert.h"

実装して、

TempConvertSoap *soap = [TempConvert TempConvertSoap];
soap.logXMLInOut = YES;

TempConvert_CelsiusToFahrenheit *p = [[TempConvert_CelsiusToFahrenheit alloc] init];
p.Celsius = @"30";

TempConvertSoapResponse *response = [soap CelsiusToFahrenheitUsingParameters:p];
NSLog(@"res:%@",response);

実行するものの、なにか問題が起きてる。

<?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>
    <soap:Fault>
      <soap:Code>
        <soap:Value>soap:Sender</soap:Value>
      </soap:Code>
      <soap:Reason>
        <soap:Text xml:lang="en">Unable to handle request without a valid action parameter. Please supply a valid soap action.</soap:Text>
      </soap:Reason>
      <soap:Detail />
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

この時点でさっぱりわからなくなったんだけど、実行例と比べてみると入力が微妙に異なる。

<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:TempConvert="http://tempuri.org/" xsl:version="1.0">
  <soap:Body>
    <CelsiusToFahrenheit xsi:type="TempConvert:CelsiusToFahrenheit">
      <Celsius>30</Celsius>
    </CelsiusToFahrenheit>
  </soap:Body>
</soap:Envelope>


WSDLから自動生成されたソースを修正。これ合ってるの?

// TempConvert.m
// @implementation TempConvert_CelsiusToFahrenheit
// - (xmlNodePtr)xmlNodeForDoc:(xmlDocPtr)doc elementName:(NSString *)elName

// 266行目
//	xmlSetNsProp(node, xsi, (const xmlChar*)"type", (const xmlChar*)"TempConvert:CelsiusToFahrenheit");
    xmlNsPtr xmlns = xmlSearchNs(doc, root, (const xmlChar*)"xmlns");
    xmlSetNsProp(node, xmlns, (const xmlChar*)"xmlns", (const xmlChar*)"http://tempuri.org/");

再度実行すると

TempConvertSoapResponse *response = [soap CelsiusToFahrenheitUsingParameters:p];
//    NSLog(@"res:%@",response);
//    NSLog(@"body: %@",response.bodyParts);

TempConvert_CelsiusToFahrenheitResponse *result = [response.bodyParts objectAtIndex:0];
NSLog(@"%@F",result.CelsiusToFahrenheitResult);
// => 2013-10-04 23:09:50.059 xmlwebsvc-test[767:c303] 86F


通りました。

ソース

まとめ

SOAPのやりとりがめんどくさい。
HTTP Postでも呼び出せるならそっちの方がいいんじゃないかと思う。そこでRESTの登場ですよってことなのかな。


VisualStudioだとサービス参照追加するだけで済むのでらくちん。

var client = new TempConvert.TempConvertSoapClient("TempConvertSoap");
string fahrenheit = client.CelsiusToFahrenheit("30");
System.Diagnostics.Debug.WriteLine(string.Format("{0}F", fahrenheit));
// => 86F

だからこそ、裏側をなにも知らなかったんだな。

参考サイト

wsdl2objc
WSDLからobjcのソースを生成するMacのアプリ。ソースが古いんだけど、大丈夫なんだか。
いやそれ以前に、生成されたコードに手を加えずに組み込めないものか。


W3Schools
チュートリアルみたいなのがいっぱいあったサイト。


WebserviceX.NET
使わなかったけど、webサービスがいっぱい登録されてて検索できるみたい。

*1:アプリには「Path or URL」と書いてあるけどURLの直接参照ではうまく行かなかったので、一旦WSDLをローカルに保存した