Using NSXMLParser initWithStream: no parser delegate methods received

后端 未结 2 896
难免孤独
难免孤独 2021-01-03 07:20

The basic problem I\'m working on is using the NSStream classes to parse incoming incremental XML data. The data is never a complete XML Document, but I want to

相关标签:
2条回答
  • 2021-01-03 07:49

    I figured out what the problem was and answering it here incase anyone else runs into this problem in the future since +[NSXMLParser initWithStream] doesn't have a lot lot of documentation out there.

    I needed to call -[NSXMLParser parse] right after I allocate NSXMLParser and set myself as delegate. But because it's a synchronous function, I need to call it another thread so I don't block the current thread and it can receive the NSStream events. I also don't need to make myself the delegate for NSInputStream.

    This can be done pretty simply using Grand Central Dispatch (GCD) like so:

    // alloc and init the xml parser
    xmlParser = [[NSXMLParser alloc] initWithStream:inputStream];
    [xmlParser setDelegate:self];
    
    // block to execute
    dispatch_block_t dispatch_block = ^(void)
    {
        [xmlParser parse];
    };
    
    // create a queue with a unique name
    dispatch_queue_t dispatch_queue = dispatch_queue_create("parser.queue", NULL);
    
    // dispatch queue
    dispatch_async(dispatch_queue, dispatch_block);
    
    // cleanup
    dispatch_release(dispatch_queue);
    

    And here is the complete working example, just incase anyone wasn't able to follow what I posted above.

    ContentParser.h

    @interface ContentParser : NSObject <NSStreamDelegate, 
                                         NSXMLParserDelegate>
    {
       NSInputStream *inputStream;
       NSOutputStream *outputStream;
       NSMutableData *receivedData;
       NSXMLParser *xmlParser;
    }
    - (void)initStream;
    

    ContentParser.m

    @implementation ContentParser
    
    - (void)initStream
    {    
       CFReadStreamRef readStream;
       CFWriteStreamRef writeStream;
    
       CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, 
                                         (CFStringRef)@"<hostname>", 
                                         <port>, 
                                         &readStream, 
                                         &writeStream);
    
       inputStream = (__bridge NSInputStream *)readStream;
       outputStream = (__bridge NSOutputStream *)writeStream;
    
       outputStream.delegate = self;
    
       [inputStream  scheduleInRunLoop:[NSRunLoop currentRunLoop]
                               forMode:NSDefaultRunLoopMode];
       [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] 
                               forMode:NSDefaultRunLoopMode];
    
       [inputStream open];
       [outputStream open];
    
       xmlParser = [[NSXMLParser alloc] initWithStream:inputStream];
       [xmlParser setDelegate:self];
    
       dispatch_block_t dispatch_block = ^(void)
       {
          [xmlParser parse];
       };
       dispatch_queue_t dispatch_queue = dispatch_queue_create("parser.queue", NULL);
       dispatch_async(dispatch_queue, dispatch_block);
       dispatch_release(dispatch_queue);
    }
    
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
                                            namespaceURI:(NSString *)namespaceURI 
                                           qualifiedName:(NSString *)qName 
                                          attributes:(NSDictionary *)attributeDict
    {
       dispatch_block_t dispatch_block = ^(void)
       {
          NSLog(@"didStartElement: %@, attributeDict: %@", 
             elementName, attributeDict);
       };
       dispatch_queue_t main_queue = dispatch_get_main_queue();
       dispatch_async(main_queue, dispatch_block);
    }
    
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    {
       dispatch_block_t dispatch_block = ^(void)
       {
          NSLog(@"foundCharacters: %@", string);
       };
       dispatch_queue_t main_queue = dispatch_get_main_queue();
       dispatch_async(main_queue, dispatch_block);
    }
    
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
                                          namespaceURI:(NSString *)namespaceURI 
                                         qualifiedName:(NSString *)qName
    {
       dispatch_block_t dispatch_block = ^(void)
       {
          NSLog(@"didEndElement: %@", elementName);
       };
       dispatch_queue_t main_queue = dispatch_get_main_queue();
       dispatch_async(main_queue, dispatch_block);
    }
    
    - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
    {
       dispatch_block_t dispatch_block = ^(void)
       {
          NSLog(@"Error %ld, Description: %@, Line: %ld, Column: %ld", 
             [parseError code], [[parser parserError] localizedDescription], 
             [parser lineNumber], [parser columnNumber]);
       };
       dispatch_queue_t main_queue = dispatch_get_main_queue();
       dispatch_async(main_queue, dispatch_block);
    }   
    
    - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
    {
       switch (eventCode) {
          case NSStreamEventHasSpaceAvailable:
          {
             /* write bytes to socket */
             break;
          }
          default:
             break;
        }
    }
    
    @end
    
    0 讨论(0)
  • 2021-01-03 07:52

    I'm not sure about this, but I don't think you need to send parse every time the stream reports an event -- the initWithStream: docs imply that the parser itself will handle the input. I think you would just do:

    xmlParser = [[NSXMLParser alloc] initWithStream:inputStream];
    [xmlParser setDelegate:self];
    [xmlParser parse];
    

    and not worry about the stream delegate method. When you get parserDidEndDocument:, you can shut down the stream.

    0 讨论(0)
提交回复
热议问题