NSXMLParser Simple Example
Asked Answered
H

4

45

Most of the examples of how to invoke the NSXMLParser are contained within complex projects involving Apps. What does a simple example that demonstrates the callbacks look like.

Hoyden answered 15/2, 2014 at 0:12 Comment(3)
Stack Overflow is, fundamentally and absolutely, a question/answer site. You may answer your own questions but you are supposed to still follow to form of the site. Your post is helpful (thank you!) but it is NOT in line with Stack Overflow's intent. Please don't be so defensive, and refactor your post in line with the SO practices.Finesse
Noted will stick to more of a Q&A style next time. As for GitHub I don't know how to use it.Hoyden
Can you please copy the solution into an answer below so we can upvote it and get this off the unanswered list? Thank you.Tern
H
16

As part of exploring the NSXMLParser I created the following really simple code.

main.m

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        NSLog(@"Main Started");

        NSError *error = nil;

        // Load the file and check the result
        NSData *data = [NSData dataWithContentsOfFile:@"/Users/Tim/Documents/MusicXml/Small.xml"
                                          options:NSDataReadingUncached
                                            error:&error];
        if(error) {
            NSLog(@"Error %@", error);

            return 1;
        }


        // Create a parser and point it at the NSData object containing the file we just loaded
        NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];

        // Create an instance of our parser delegate and assign it to the parser
        MyXmlParserDelegate *parserDelegate = [[MyXmlParserDelegate alloc] init];
        [parser setDelegate:parserDelegate];

        // Invoke the parser and check the result
        [parser parse];
        error = [parser parserError];
        if(error)
        {
            NSLog(@"Error %@", error);

            return 1;
        }

        // All done
        NSLog(@"Main Ended");
    }
    return 0;
}

MyXmlParserDelegate.h

#import <Foundation/Foundation.h>

@interface MyXmlParserDelegate : NSObject <NSXMLParserDelegate>

@end

MyXmlParserDelegate.m

#import "MyXmlParserDelegate.h"

@implementation MyXmlParserDelegate

- (void) parserDidStartDocument:(NSXMLParser *)parser {
    NSLog(@"parserDidStartDocument");
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
    NSLog(@"didStartElement --> %@", elementName);
}

-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    NSLog(@"foundCharacters --> %@", string);
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    NSLog(@"didEndElement   --> %@", elementName);
}

- (void) parserDidEndDocument:(NSXMLParser *)parser {
    NSLog(@"parserDidEndDocument");
}
@end

I've posted it in the hope that it helps someone else.

Hoyden answered 25/12, 2014 at 23:9 Comment(0)
A
3

Swift 4 Example - Parsing Xib-file.

import Foundation

class XMLTransformer: NSObject {

   private let parser: XMLParser
   private var stack = [Node]()
   private var tree: Node?

   init(data: Data) {
      parser = XMLParser(data: data)
      super.init()
      parser.delegate = self
   }
}

extension XMLTransformer {

   func transform() throws -> Node? {
      parser.parse()
      if let e = parser.parserError {
         throw e
      }
      assert(stack.isEmpty)
      assert(tree != nil)
      return tree
   }

}

extension XMLTransformer: XMLParserDelegate {

   func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
      guard let tag = Tag(rawValue: elementName) else {
         return
      }
      let node = Node(tag: tag, attributes: attributeDict, nodes: [])
      stack.append(node)
   }

   func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
      guard let tag = Tag(rawValue: elementName) else {
         return
      }
      let lastElement = stack.removeLast()
      assert(lastElement.tag == tag)
      if let last = stack.last {
         last.nodes += [lastElement]
      } else {
         tree = lastElement
      }
   }
}

extension XMLTransformer {

   enum Tag: String {
      case document
      case objects
      case tableViewCell, tableViewCellContentView
      case subviews
      case mapView
      case constraints, constraint
      case connections, outlet
   }
}

extension XMLTransformer {

   class Node {

      let tag: Tag
      let attributes: [String : String]
      var nodes: [Node]

      init(tag: Tag, attributes: [String : String], nodes: [Node] = []) {
         self.tag = tag
         self.attributes = attributes
         self.nodes = nodes
      }
   }
}

Usage:

let data = try xib(named: "MapTableViewCell")
let c = XMLTransformer(data: data)
let tree = try c.transform() // Here you have parsed XML in a Tree representation.
Administration answered 31/8, 2017 at 19:15 Comment(0)
B
1
#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate, NSXMLParserDelegate>
@property (nonatomic, strong) NSMutableDictionary *dictXML;
@property (nonatomic,strong) NSMutableArray *arrOfUpdateDictsByVersion;
@property (nonatomic,strong) NSString *strElementBeingParsed;
@property (nonatomic,strong) NSString *strElementFinishedParsing;

@end


#import "AppDelegate.h"

@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end

@implementation AppDelegate

@synthesize dictXML;
@synthesize arrOfUpdateDictsByVersion;

@synthesize strElementBeingParsed;
@synthesize strElementFinishedParsing;


- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application

    [self initializeTheArray];
    [self startParsingXML];
}

-(void)initializeTheArray{
    self.dictXML = [[NSMutableDictionary alloc] init];
    self.arrOfUpdateDictsByVersion = [[NSMutableArray alloc] init];
}

-(void)startParsingXML{
//    NSXMLParser *xmlparser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:@"http://cdn.example.com/databaseupdate.xml"]];
    NSXMLParser *xmlparser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL fileURLWithPath:@"/Users/vkrmsinha/Desktop/xmlParse.xml"]];
    [xmlparser setDelegate:self];
    [xmlparser parse];
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict;
{
    // initial tag comes in here
    self.strElementBeingParsed = elementName;
    /*if([elementName isEqualToString:@"Version"]){

    }
    else if ([elementName isEqualToString:@"Update"]){

    }*/
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
{

    if(([string rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound) && ![self.strElementBeingParsed isEqualToString:@"Update"])
        return;

    // middle part from between the start and end tags comes here
    if ([self.strElementBeingParsed isEqualToString:@"Update"]){
        NSMutableDictionary *dictUpdate = [NSMutableDictionary dictionary];
        [self.arrOfUpdateDictsByVersion addObject:dictUpdate];
    }
    else if ([self.strElementBeingParsed isEqualToString:@"UpdateType"]){
        NSMutableDictionary *dictUpdate = [self.arrOfUpdateDictsByVersion lastObject];
        NSMutableDictionary *dictUpd = [NSMutableDictionary dictionary];
        [dictUpd setValue:string forKey:@"UpdateType"];
        [dictUpdate setValue:dictUpd forKey:@"update"];
    }
    else if([self.strElementBeingParsed isEqualToString:@"Version"]){
        NSMutableDictionary *dictUpdate = [self.arrOfUpdateDictsByVersion lastObject];
        // WARNING: ASK IF NO TWO VERSION WILL BE SAME IN FUTURE
        [dictUpdate setValue:string forKey:@"version"];
    }
    else if ([self.strElementBeingParsed isEqualToString:@"FileName"]){
        NSMutableDictionary *dictUpdate = [self.arrOfUpdateDictsByVersion lastObject];
        NSMutableDictionary *dict = [dictUpdate objectForKey:@"update"];
        [dict setValue:string forKey:@"FileName"];
    }
    else if ([self.strElementBeingParsed isEqualToString:@"Hash"]){
        NSMutableDictionary *dictUpdate = [self.arrOfUpdateDictsByVersion lastObject];
        NSMutableDictionary *dict = [dictUpdate objectForKey:@"update"];
        [dict setValue:string forKey:@"Hash"];
    }
    else if ([self.strElementBeingParsed isEqualToString:@"DownloadURL"]){
        NSMutableDictionary *dictUpdate = [self.arrOfUpdateDictsByVersion lastObject];
        NSMutableDictionary *dict = [dictUpdate objectForKey:@"update"];
        [dict setValue:string forKey:@"DownloadURL"];
    }
    else if ([self.strElementBeingParsed isEqualToString:@"Size"]){
        NSMutableDictionary *dictUpdate = [self.arrOfUpdateDictsByVersion lastObject];
        NSMutableDictionary *dict = [dictUpdate objectForKey:@"update"];
        [dict setValue:string forKey:@"Size"];
    }

}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
{
    // ending of tag comes in here
    self.strElementFinishedParsing = elementName;
    if([elementName isEqualToString:@"Update"]){
        [self.arrOfUpdateDictsByVersion sortUsingDescriptors:[NSArray arrayWithObjects:[NSSortDescriptor sortDescriptorWithKey:@"self.version" ascending:YES], nil]];
        NSLog(@"%@", [self.arrOfUpdateDictsByVersion lastObject]);

    }
    if([elementName isEqualToString:@"UpdateDetails"]){
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        [dict setValue:[[self.arrOfUpdateDictsByVersion lastObject] objectForKey:@"version"] forKey:@"latestVer"];
        [dict setValue:[[self.arrOfUpdateDictsByVersion firstObject] objectForKey:@"version"] forKey:@"oldestVer"];
        [dict setValue:self.arrOfUpdateDictsByVersion forKey:@"arrOfUpdsByVer"];
        NSLog(@"%@", dict);
    }

}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}


<UpdateDetails xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <DatabaseUpdates>
    <Update>
     <UpdateType>CompleteDatabase</UpdateType>
     <Version>1</Version>
     <FileName>1completedatabase.zip</FileName>
     <Hash>ad94431d2fe4cd60eb3347fadaa45d88</Hash>
     <DownloadURL>
      http://www.example.com/download/new.xml
     </DownloadURL>
     <Size>2367008</Size>
    </Update>
</DatabaseUpdates>
</UpdateDetails>
Bradwell answered 17/12, 2018 at 12:28 Comment(0)
H
0

Here is a Swift version of the original Objective-C code below.

It was built and tested using XCode 7.3. In writing the delegate I found it quite handy to copy the function prototypes from the documentation. It is worth noting that Swift is currently a fairly rapidly moving target.

main.swift

import Foundation

// Let's go
print("Main: Started")

// Try to load the file. Display the description of the error if one occurs
var xmlData : NSData
do {
    xmlData = try NSData(contentsOfFile: "/Users/amt/Documents/TestXml/Test.xml",
                         options: .DataReadingMappedIfSafe)
}
catch let error as NSError {
    print("Main: \(error.description)")

    exit(1)
}

// Create a parser and point it at the NSData object containing
// the file we just loaded
var parser : NSXMLParser! = NSXMLParser(data: xmlData)

// Create a parser delegate object and assign it to the parser
// Beware the "weak" reference and don't try to combine the two lines
// of code into one unless you like EXC_BAD_ACCESS exceptions
var parserDelegate : MyXmlParserDelegate = MyXmlParserDelegate()
parser.delegate = parserDelegate

// This example also illustrates some of the namespace functions defined in
// the delegate protocol so enable namespace reporting to see them invoked
parser.shouldReportNamespacePrefixes = true

// Parse the document
if !parser.parse() {
    // If parse() returned false then an error occurred so display is location
    // and details
    let error = parser.parserError
    let line = parser.lineNumber
    let col = parser.columnNumber
    print("Parsing failed at \(line):\(col): \(error?.localizedDescription)")
}

// All done
print("Main: Ended")
exit(0)

MyXmlParserDelegate.swift

import Foundation

class MyXmlParserDelegate:NSObject, NSXMLParserDelegate {

    @objc func parserDidStartDocument(parser: NSXMLParser) {
        print("parserDidStartDocument")
    }

    @objc func parser(parser: NSXMLParser, didStartElement elementName: String,
               namespaceURI: String?, qualifiedName qName: String?,
               attributes attributeDict: [String : String]) {
        print("didStartElement       --> \(elementName)")
    }

    @objc func parser(parser: NSXMLParser, foundCharacters string: String) {
        print("foundCharacters       --> \(string)")
    }

    @objc func parser(parser: NSXMLParser, didEndElement elementName: String,
               namespaceURI: String?, qualifiedName qName: String?) {
        print("didEndElement         --> \(elementName)")
    }

    @objc func parser(parser: NSXMLParser, didStartMappingPrefix prefix: String,
               toURI namespaceURI: String) {
        print("didStartMappingPrefix --> Prefix: \(prefix) toURI: \(namespaceURI)")
    }

    @objc func parser(parser: NSXMLParser, didEndMappingPrefix prefix: String) {
        print("didEndMappingPrefix   --> Prefix: \(prefix)")
    }

    @objc func parserDidEndDocument(parser: NSXMLParser) {
        print("parserDidEndDocument")
    }
}
Hoyden answered 26/4, 2016 at 7:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.