SOAPで取得したデータをJSON形式に変換します。エスケープ文字、空オブジェクト、空の配列には未対応です。コードも怪しいです。
以下のようなファイルを用意します。
- soap.xml ... 取得したSOAP形式のデータ(今回はファイルで)
- LHParser.h/m ... XMLのパースとJSONへの変換
- NSMutableArray+StackAdditions.h/m ... NSMutableArrayにスタックの機能を追加したカテゴリ
- libxml2.dylib ... XMLパース用のライブラリ。事前に追加。
soap.xml
<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:Body>
<ns1:getHTTMembersResponse
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://k-on.luckyandhappy.com">
<response xsi:type="ns2:GetHTTMembersResponse"
xmlns:ns2="k-on">
<members
soapenc:arrayType="ns2:Member[1]"
xsi:type="soapenc:Array"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<member xsi:type="ns2:Member">
<name xsi:type="xsd:string">平沢 唯</name>
<gender xsi:type="ns2:Gender">
<id xsi:type="xsd:int">2</id>
<title xsi:type="xsd:string">女性</title>
</gender>
<birthday xsi:type="xsd:dateTime">1991-11-27T00:00:00.000Z</birthday>
<height xsi:type="xsd:float">156.2</height>
<weight xsi:type="xsd:float">50.5</weight>
<abo xsi:type="xsd:ABO">
<id xsi:type="xsd:int">3</id>
<title xsi:type="xsd:string">O</title>
</abo>
<isVocal xsi:type="xsd:boolean">true</isVocal>
</member>
</members>
</response>
</ns1:getHTTMembersResponse>
</soapenv:Body>
</soapenv:Envelope>
LHParser.h
#import <Foundation/Foundation.h>
#import <libxml/tree.h>
#import "NSMutableArray StackAdditions.h"
@interface LHParser : NSObject {
NSDictionary *_openers;
NSDictionary *_closures;
NSMutableArray *_closuresStack;
NSDictionary *_charactersFoundTable;
BOOL _isCharactersFound;
xmlParserCtxtPtr _ctxt;
NSMutableString *_json;
}
-(void)parse;
-(void)didParse;
-(void)startElement:(NSString*)localname type:(NSString*)type;
-(void)endElement:(NSString*)localname;
-(void)foundCharacter:(NSString*)characters;
@end
static void startElementSAX(void *context,
const xmlChar *localname,
const xmlChar *prefix,
const xmlChar *URI,
int nb_namespaces,
const xmlChar **namespaces,
int nb_attributes,
int nb_defaulted,
const xmlChar **atrributes);
static void endElementSAX(void *context,
const xmlChar *localname,
const xmlChar *prefix,
const xmlChar *URI);
static void charactersFoundSAX(void *context,
const xmlChar *characters,
int lenght);
LHParser.m
#import "LHParser.h"
static xmlSAXHandler simpleSAXHandlerStruct;
@implementation LHParser
-(id)init
{
self = [super init];
if(self) {
_openers = [NSDictionary dictionaryWithObjectsAndKeys:
@"\"", @"string",
@"\"", @"dateTime",
@"", @"int",
@"", @"float",
@"", @"boolean",
@"[", @"Array",
@"{", @"object",
nil];
_closures = [NSDictionary dictionaryWithObjectsAndKeys:
@"\"", @"\"",
@"}", @"{",
@"]", @"[",
@"", @"",
nil];
_charactersFoundTable = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], @"string",
[NSNumber numberWithBool:YES], @"dateTime",
[NSNumber numberWithBool:YES], @"int",
[NSNumber numberWithBool:YES], @"float",
[NSNumber numberWithBool:YES], @"boolean",
[NSNumber numberWithBool:NO], @"Array",
nil];
}
return self;
}
-(void)parse
{
_isCharactersFound = NO;
_closuresStack = [[NSMutableArray alloc] init];
_json = [[NSMutableString alloc] initWithString:@"{"];
xmlDefaultSAXHandlerInit();
_ctxt = xmlCreatePushParserCtxt(&simpleSAXHandlerStruct,self,NULL,0,NULL);
NSString *path = [[NSBundle mainBundle] pathForResource:@"soap" ofType:@"xml"];
NSData *data = [NSData dataWithContentsOfFile:path];
xmlParseChunk(_ctxt, (const char*)[data bytes], [data length], 0);
xmlParseChunk(_ctxt,NULL, 0, 1);
xmlFreeParserCtxt(_ctxt);
}
-(void)startElement:(NSString*)localname type:(NSString*)type
{
NSLog(@"localname=%@", localname);
NSLog(@"type=%@", type);
NSString *opener = [_openers valueForKey:type];
if(opener==nil) {
opener = [_openers valueForKey:@"object"];
}
NSString *parent = [_closuresStack lastObject];
BOOL inArray = NO;
if(parent){
inArray = [parent isEqualToString:@"]"];
}
if(!inArray) {
[_json appendString:@"\""];
[_json appendString:localname];
[_json appendString:@"\""];
[_json appendString:@":"];
}
[_json appendString:opener];
NSString *closure = [_closures valueForKey:opener];
[_closuresStack push:closure];
NSNumber *boolean = [_charactersFoundTable valueForKey:type];
if(boolean!=nil) {
_isCharactersFound = [boolean boolValue];
}
else {
_isCharactersFound = NO;
}
}
-(void)endElement:(NSString*)localname
{
_isCharactersFound = NO;
NSString *closure = [_closuresStack pop];
if( [closure isEqualToString:@"}"]
|| [closure isEqualToString:@"]"]) {
[_json deleteCharactersInRange:NSMakeRange([_json length]-1, 1)];
}
[_json appendString:closure];
[_json appendString:@","];
}
-(void)foundCharacter:(NSString*)characters
{
if(_isCharactersFound) {
NSLog(@"%@", characters);
[_json appendString:characters];
}
}
-(void)didParse
{
[_json deleteCharactersInRange:NSMakeRange([_json length]-1, 1)];
[_json appendString:@"}"];
NSLog(@"didParse");
NSLog(@"%@", _json);
}
-(void)dealloc
{
[_openers release];
[_closures release];
[_closuresStack release];
[_charactersFoundTable release];
[_json release];
}
@end
#pragma mark SAX Parsing Callbacks
bool isEnvelope = false;
bool isBody = false;
bool isContent = false;
static const char* ENVELOPE = "Envelope";
static const char* ENVELOPE_URI = "http://schemas.xmlsoap.org/soap/envelope/";
static const char* BODY = "Body";
static const char* CONTENT_URI = "http://k-on.luckyandhappy.com";
static void startElementSAX(void *context,
const xmlChar *localname,
const xmlChar *prefix,
const xmlChar *URI,
int nb_namespaces,
const xmlChar **namespaces,
int nb_attributes,
int nb_defaulted,
const xmlChar **attributes)
{
NSLog(@"\n");
if( 0==strncmp((const char*)localname, ENVELOPE, strlen(ENVELOPE)-1)
&& 0==strncmp((const char*)URI, ENVELOPE_URI, strlen(ENVELOPE_URI)-1)
){
NSLog(@"Envelope start");
isEnvelope = true;
}
if( 0==strncmp((const char*)localname, BODY, strlen(BODY)-1)
&& 0==strncmp((const char*)URI, ENVELOPE_URI, strlen(ENVELOPE_URI)-1)
){
NSLog(@"Body start");
isBody = true;
}
if( NULL != URI
&& 0==strncmp((const char*)URI, CONTENT_URI, strlen(CONTENT_URI)-1)
){
NSLog(@"Content start");
isContent = true;
}
if(isContent){
for(int i=0; i<nb_attributes; i) {
if(0==strncmp((const char*)attributes[0], "type", strlen("type")-1)){
char type[128];
int len = attributes[4] - attributes[3];
memcpy( type, attributes[3], len );
type[len] = '\0';
const char *sp = ":";
char *type1;
char *type2;
type1 = strtok( type, sp );
type2 = strtok( NULL, sp );
NSString *nsLocalname = [NSString stringWithCString:(const char*)localname
encoding:NSUTF8StringEncoding];
NSString *nsType = [NSString stringWithCString:(const char*)type2
encoding:NSUTF8StringEncoding];
[(LHParser*)context startElement:nsLocalname type:nsType];
break;
}
attributes = 5;
}
}
}
static void endElementSAX(void *context,
const xmlChar *localname,
const xmlChar *prefix,
const xmlChar *URI)
{
if( 0==strncmp((const char*)localname, ENVELOPE, strlen(ENVELOPE)-1)
&& 0==strncmp((const char*)URI, ENVELOPE_URI, strlen(ENVELOPE_URI)-1)
){
isEnvelope = false;
}
if( 0==strncmp((const char*)localname, BODY, strlen(BODY)-1)
&& 0==strncmp((const char*)URI, ENVELOPE_URI, strlen(ENVELOPE_URI)-1)
){
isBody = false;
}
if( NULL != URI
&& 0==strncmp((const char*)URI, CONTENT_URI, strlen(CONTENT_URI)-1)
){
isContent = false;
[(LHParser*)context didParse];
}
if(isContent){
NSString *nsLocalname = [NSString stringWithCString:(const char*)localname
encoding:NSUTF8StringEncoding];
[(LHParser*)context endElement:nsLocalname];
}
}
static void charactersFoundSAX(void *context,
const xmlChar *characters,
int lenght)
{
NSString *nsCharacters = [[NSString alloc] initWithBytes:(const char*)characters
length:lenght
encoding:NSUTF8StringEncoding];
[(LHParser*)context foundCharacter:nsCharacters];
[nsCharacters release];
}
static void errorEncounteredSAX(void *ctx,
const char *msg,
...)
{
NSCAssert(NO, @"Unhandled error encountered during SAX parse.");
}
static xmlSAXHandler simpleSAXHandlerStruct = {
NULL, /* internalSubset */
NULL, /* isStandalone */
NULL, /* hasInternalSubset */
NULL, /* hasExternalSubset */
NULL, /* resolveEntity */
NULL, /* getEntity */
NULL, /* entityDecl */
NULL, /* notationDecl */
NULL, /* attributeDecl */
NULL, /* elementDecl */
NULL, /* unparsedEntityDecl */
NULL, /* setDocumentLocator */
NULL, /* startDocument */
NULL, /* endDocument */
NULL, /* startElement*/
NULL, /* endElement */
NULL, /* reference */
charactersFoundSAX, /* characters */
NULL, /* ignorableWhitespace */
NULL, /* processingInstruction */
NULL, /* comment */
NULL, /* warning */
errorEncounteredSAX, /* error */
NULL, /* fatalError //: unused error() get all the errors */
NULL, /* getParameterEntity */
NULL, /* cdataBlock */
NULL, /* externalSubset */
XML_SAX2_MAGIC, //
NULL,
startElementSAX, /* startElementNs */
endElementSAX, /* endElementNs */
NULL, /* serror */
};
NSMutableArray+StackAdditions.h
#import <Foundation/Foundation.h> @interface NSMutableArray (StackAdditions) - (id)pop; - (void)push:(id)obj; @end
NSMutableArray+StackAdditions.m
#import "NSMutableArray StackAdditions.h"
@implementation NSMutableArray (StackAdditions)
- (id)pop
{
id lastObject = [[[self lastObject] retain] autorelease];
if (lastObject)
[self removeLastObject];
return lastObject;
}
- (void)push:(id)obj
{
[self addObject: obj];
}
@end
呼び出しコード
LHParser *parser = [[LHParser alloc] init];
[parser parse];
出力結果(実際は1行です)
{"response":
{
"members":
[
{
"name":"平沢 唯",
"gender":{"id":2,"title":"女性"},
"birthday":"1991-11-27T00:00:00.000Z",
"height":156.2,
"weight":50.5,
"abo":{"id":3,"title":"O"},
"isVocal":true
}
]
}
}
0 件のコメント:
コメントを投稿