Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion Groups
General
GeneralPortable MacsHardwareNetworking
Applications
Mac ApplicationsEudoraFirefox / MozillaInternet ExplorerOutlook ExpressMS OfficeEntourageExcelPowerPointWordVirtual PCMedia PlayerOther MS Products
Programming
Mac ProgrammingCodeWarriorPerl
Country Specific
Australian Mac GroupUK Mac Group

Mac Forum / Programming / Mac Programming / August 2005



Tip: Looking for answers? Try searching our database.

NSXMLParser; getting the content of elements

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Davide Benini - 12 Aug 2005 10:09 GMT
Hi folks. I am building a class that imports a filemaker database into a
  core data database. I managed to get a "hand-taylored" xml from
filemaker, in order to match the core data entity fields.
I am trying to use the NSXMLParser class; I considered using XML Tree
Programming, but the parsing process seems more fit, since I am just
adding a NSManagedObject for each xml elements, using its child elements
as attributes. (incidentally, what do you say about that?)
And now the problem.
My xml file is like:

<bibliography>
    <entry>
        <author>John Doe</author>
        <title>My life</title>
    </entry>
    ...ecc
</bibliography>

I inizialized the parser, and used the method
parser:didStartElement:namespaceURI:qualifiedName:attributes:

I am sure the parser is getting all of the entity elements,and all of
its child elements (I verified that with a NSLog, and the debugger), but
I can't find any way to get the element content. How do I get the string
"John Doe"?
Thanks in adavance
Davide
David Phillip Oster - 12 Aug 2005 17:07 GMT
> Hi folks. I am building a class that imports a filemaker database into a
>    core data database. I managed to get a "hand-taylored" xml from
[quoted text clipped - 16 lines]
> I inizialized the parser, and used the method
> parser:didStartElement:namespaceURI:qualifiedName:attributes:

OK, at that point, if the element is author, you set an isInAuthor flag.
You will clear that flag later in your
parser:didEndElement:namespaceURI:qualifiedName: handler.

When your delegate gets called with parser:foundCDATA:
if the isInAuthor flag is YES, then the CDATA you get passed will be
"John Doe".

One problem with your scheme is that it isn't very future proof: you'll
only see what you are looking for. Suppose your program gets revised and
version 1.5 adds some new node types and attributes, you'd like version
1 to leave this additional material attached to the tree, and when you
save, preserve the new material.

If you use the NSXML class, you get the whole tree, even the unkown
nodes. If you are writing an editor, you probably want NSXML. I'd use
NSXMLParser if I were writing a program that processed an XML tree into
a different data structure then quit. However, if I were just processing
an XML tree into a different file format, I'd probably just use XSLT

I haven't yet tried using NSXML with Core Data, but I have used it with
NSOutlineView. That is really straightforward and easy.

here is a sample data file that my program expects:
<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.0">
 <head>
   <title>Untitled</title>
   <expansionState>1</expansionState>
 </head>
 <body>
   <outline text="testing testing"/>
   <outline text="colors">
     <outline text="red"/>
     <outline text="green"/>
     <outline text="blue"/>
   </outline>
 </body>
</opml>

This is the code that extracts the value of a text attribute for a node:
- (id) outlineView: (NSOutlineView*) olv objectValueForTableColumn:
(NSTableColumn*) tableColumn byItem: (id) item {
  NSXMLElement*  node = [self pNodeOfItem: item];
  NSError* err;
  NSString*   str = @"";
  NSArray* ar = [node objectsForXQuery: [tableColumn identifier] error:
&err];  // @"./@text"
  if(nil != ar && 0 < [ar count]){
     str = [(NSXMLNode*)[ar objectAtIndex: 0] stringValue];
  }
  return str;
}

- (NSXMLElement*) pNodeOfItem: (id) item {
  return (nil != item) ? (NSXMLElement*) item : mXMLBody;
}

This is how the tree of <outline> nodes gets walked.

- (id) outlineView: (NSOutlineView*) olv child: (int) index ofItem: (id)
item {
  NSXMLElement*  node = [self pNodeOfItem: item];
  return [node childAtIndex: index];
}

- (int) outlineView: (NSOutlineView*) olv numberOfChildrenOfItem: (id)
item {
  NSXMLElement*  node = [self pNodeOfItem: item];
  return [node childCount];
}

And here is how the tree gets read in:
- (BOOL) readFromURL: (NSURL*) absoluteURL ofType: (NSString*) typeName
error: (NSError**) outError {
  NSXMLDocument* xmlDoc = nil;
  if(nil != (xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:
absoluteURL options: 0 error: outError])){
     [mXMLDoc release];
     mXMLDoc = xmlDoc;
     mXMLBody = [[xmlDoc objectsForXQuery: @".//body" error: outError]
objectAtIndex: 0];
     return YES;
  }
   return NO;
}

writing the data out is similarly tiny:

- (NSData*) dataRepresentationOfType: (NSString*) aType {
   return [mXMLDoc XMLData];
}

Signature

David Phillip Oster

Davide Benini - 12 Aug 2005 19:21 GMT
Thanks a million David.
When you say a flag, I guess you talk about a boolean flag... if so, I
think I got it clear now. Thanks.
I am actually using the NSXMLParser to move some data from a FileMaker
database to this application I am writing.
About future proofness: I've built a method that returns an array of the
name of all the attributes of a given CoreData entity; so I was thinking
of encapsluating the parser:didStartElement: methond in a cicle.
So:

-parser:didStartElement: finds an element corresponding to an attribute
(if ot is the entity name, it just adds a new entity)
-the element name is assigned to an instance variable
-parser:foundCDATA: writes the data to the attribute
-parser:didEndElement releases the instances if necessary
This should work... If it does, I'll post the method.
Thanks again
Davide

>>Hi folks. I am building a class that imports a filemaker database into a
>>   core data database. I managed to get a "hand-taylored" xml from
[quoted text clipped - 109 lines]
>     return [mXMLDoc XMLData];
> }
Doc O'Leary - 12 Aug 2005 23:00 GMT
> I am sure the parser is getting all of the entity elements,and all of
> its child elements (I verified that with a NSLog, and the debugger), but
> I can't find any way to get the element content. How do I get the string
> "John Doe"?

Depending on your real needs, check out:

http://www.subsume.com/cgi-bin/go.pl?k=STXML

which gives you NSKeyValueCoding support, allowing you to do something
easy like:

NSString *authorString = [bibRoot valueForKeyPath:
@"bibliography.entry.author.contentDescription"];
Davide Benini - 13 Aug 2005 18:26 GMT
I solved the previous issue, and the routine works pretty well now :-)
I'll post it at the end of this post, for those interested in it.
I have another problem now.
The NSXML parse doesn't accept the &, è é ecc. characters; it makes
mistakes...
Is it due to the encoding? the document is latin 1 and I imported it
into the Xcode project as latin 1...
Anyway, I've written an applescript to substitute them with the HTML
entities (&amp; , ecc)
The parser now COMPLETELY IGNORES the html entities: I get blanks in the
entities place...
I am always puzzled by this encoding stuff... Any suggestion?
Cheers, Davide

PS: here follows the routine; it's still a bit rough, but it does the
job. I just post the interesting part, the NSXMLParser delegate methods.
BTW, thanks for the precious suggestion Doc, the STXML framework looks
grand; yet I wanted to see if I could do the stuff by myself :-)

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString
*)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if ( [elementName isEqualToString:@"entry"]) {
       
        currentObject = [NSEntityDescription
insertNewObjectForEntityForName:@"Entry"
                                                                 inManagedObjectContext:currentMoc];
        return;
        //[currentObject setValue:@"Pirla" forKey:@"author"];
       
        //NSLog(elementName);
    }
    NSEnumerator *propertiesEnumerator = [propertiesName objectEnumerator];
    NSString *property;
    while (property = [propertiesEnumerator nextObject]) {
        if ( [elementName isEqualToString:property]) {
            isAnAttribute = YES;
            [currentKey setString:property];
            currentString = [NSMutableString new];
        }
           
    }
       
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if (isAnAttribute) {
        [currentString appendString:string];
    }

}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString
*)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
    if (isAnAttribute) {
        //NSLog(currentString);
        [currentObject setValue:currentString forKey:currentKey];
        isAnAttribute = NO;
        [currentString release];
    }
}
David Phillip Oster - 13 Aug 2005 22:29 GMT
> I solved the previous issue, and the routine works pretty well now :-)
> I'll post it at the end of this post, for those interested in it.
[quoted text clipped - 9 lines]
> I am always puzzled by this encoding stuff... Any suggestion?
> Cheers, Davide

Your XML should have an initial line specifying the character set.
XML defaults to utf-8.
<?xml version="1.0" encoding="UTF-8"?>

You have to handle entities similar to the way you handle CDATA: you
register an appropriate callback, translate the entity, and concatentate
it to the preceding and following CDATA.

Signature

David Phillip Oster

 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.