気楽なソフト工房

プログラミングについていろいろな記事を書いています。



mykonos2008

Author:mykonos2008
システムエンジニアとして働いている30代の会社員です。
仕事や趣味でプログラムを書いている方の役に立つ記事を書いていきたいと思っています。
ご意見、ご感想はこちらまで
If you are an english speaker,Please visit my english blog.

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
前回の記事では[NSXMLParser]を使用して、XMLを解析する際の、基本的な部分を紹介させて頂きました。

本稿では具体的なXMLを使って、XMLを解析する際のテクニックをご紹介したいと思います。

サンプルのXMLは以下を使用します。

<User>
  <Id>1</Id>
  <Name>山田太郎</Name>
  <Email>xxxxx@xxxx.com</Email>
  <Friends>
    <User>
      <Id>2</Id>
    </User>
    <User>
      <Id>3</Id>
    </User>
  </Friends>
</User>

Objective-cのコードは以下です。


-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
      qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    _tagName = elementName;
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
    qualifiedName:(NSString *)qName
{
    _tagName = nil;
}

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if([_tagName isEqualToString:@"Name"]){
        NSLog(@"Name is %@",string);
    }
}


テキスト部分を処理する「(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string」にはタグ名は渡されませんので、 開始タグを処理するメソッドで、インスタンス変数[_tagName]に要素名をセットし、それを利用して処理したいタグを判別するようにしています。

NSXMLParserを使用する際に一点気をつけないといけないことがあります。NSXMLParserでは1つのテキストノードの文字列を処理する間に、「foundCharacters」が 複数回に分けてコールされることがあります。これを知らずに処理を書いていると文字列が途中で切れてしまうことになります。
それに対応するコードが以下になります。

@implementation APIResultSearializer{
    NSMutableString __strong *_stringBuffer;
}

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
    qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    _tagName = elementName;
}

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if(!_stringBuffer){
        _stringBuffer = [[NSMutableString alloc] init];
    }
    [_stringBuffer appendString:string];
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
     qualifiedName:(NSString *)qName
{    
    if(_stringBuffer){
        if([_tagName isEqualToString:@"Name"]){
            NSLog(@"Name is %@ ",_stringBuffer);         
        }
        _stringBuffer = nil;
    }    
    _tagName = nil;
}


NSMutableStringを使用して、そこにテキストを積めておいて、要素の終了タグのハンドラがコールされた時点でそれを取り出して処理します。

ここまでで、XMLから必要な要素の値を取得する処理はとりあえず完了なのですが、一点問題があります。
サンプルのXMLには<Id>1</Id>がUser直下とUser/Friends/Userの下の2カ所に存在しているため、
上記のコードのようにタグ名だけで処理をしているとどちらか片方を取りたい時に問題が発生してしまいます。

それに対応するコードが以下になります。

@implementation ParserDelegateImpl{    
    NSMutableArray *_elementStack;
    NSMutableString __strong *_stringBuffer;
}

-(id)init
{
    self = [super init];
    if(self){
        _elementStack = [NSMutableArray array];
    }
    return self;
}

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI 
   qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
     [_elementStack addObject:elementName];
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
    qualifiedName:(NSString *)qName
{    
    if(_stringBuffer){
        if([[_elementStack lastObject] isEqualToString:@"Id"] && [_elementStack count] == 2){
            NSLog(@"Id is %@ ",_stringBuffer);
        }
        else if([[_elementStack lastObject] isEqualToString:@"Id"] && [_elementStack count] == 4){
            NSLog(@"User/Friends/User/Id is %@ ",_stringBuffer);
        }
        _stringBuffer = nil;
    }
    
    [_elementStack removeLastObject];
}

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if(!_stringBuffer){
        _stringBuffer = [[NSMutableString alloc] init];
    }
    [_stringBuffer appendString:string];
}

NSMutableArrayをスタックとして使用し、開始タグでスタックにつめて、終了タグでスタックから削除するようにします。 上記のサンプルのようにスタックのサイズだけで判定するのであればint型変数を開始/終了タグのタイミングでプラスマイナスすることでも 対応可能ですが、より複雑なXMLを処理する場合は、この方法が役に立つと思います。

NSXMLParserでXMLを解析する(1)

コメント

コメントの投稿

管理者にだけ表示を許可する

トラックバック

http://csfun.blog49.fc2.com/tb.php/113-ec043c15

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。