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 件のコメント:
コメントを投稿