views:

1799

answers:

6

I'm trying to read in and parse an xml document in an iPhone app. I begin parsing and then use the override method:

   static void startElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, 
                        int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes)

I then try to convert the attributes to a string with:

   NSString *str1 = [[NSString alloc] initWithCString:attributes encoding:NSUTF8StringEncoding];

Why does the attributes parameter have two ** in front of it. And why when trying to extract the data and convert it to a string with the above code do I get the warning:

passing argument 1 of 'initWithCString:encoding:' from incompatible pointer type.

A: 

The '**' notation means "pointer to a pointer." In C/C++, a "string" is represented by an array of characters. An array is actually just a pointer under the covers, so a string in C/C++ can actually be declared as either "char[]" or "char*". The [] notation compiles down to a pointer to an array.

A common example of this is the typical "main" function in C/C++:

int main(int argc, char **argv)

Which is equivalent to:

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

argv is an array of char* "strings" (the command-line arguments to the program).

I can't provide an example at the moment, but it looks like you need to iterate over attributes to access the individual strings. For example, attributes[0] would be the first attribute string (an xmlChar*). You should be able to convert each individual attribute to an NSString.

Andy White
+2  A: 

The documentation for libxml's start element callback states that the pointer is to an array that hold 5 values for each attribute (the number of attributes is returned in nb_attributes). This means that every 5th value in the array is a new attribute item.

The five items for each attribute are:

  1. localname (the name of the attribute)
  2. prefix (the namespace of the attribute)
  3. URI
  4. [start of] value (a pointer to the start of the xmlChar string for the value)
  5. end [of value] (a pointer to the end of the xmlChar string for the value)

So you need to step through the array, get each value out of the items for the first attribute, then use the start value pointer to get the xmlChar string that is length = end - start. Then start over with the next attribute till you read in nb_attributes worth.

If that makes your head ache then I strongly suggest you switch to Apple's NSXMLParser (link may require login, or use this link NSXMLParser). In which case you would get the attributes as an NSDictionary. To get all the attributes out of it you could do the following:

for (NSString *attributeName in [attributeDict allKeys]) {
    NSString *attributeValue = [attributeDict objectForKey:attributeName];
    // do something here with attributeName and attributeValue
}

If you have access to the iPhone developer site then look at the example SeismicXML.

Nathan Kinsinger
A: 

const xmlChar **namespaces is an array of CStrings (int nb_namespaces tells you how many). If you want each namespace as an NSString, you could do something like the following:

NSMutableArray *namespaces = [[NSMutableArray alloc] init];

int i;
for (i = 0; i < nb_namespaces; i++) {
    NSString *namespace = [[NSString alloc] initWithCString:attributes[i] encoding:NSUTF8StringEncoding];
    [namespaces addObject:namespace];
}

The initWithCString method is expecting xmlChar *, which is a pointer to an xmlChar (the first char in a CString).

xmlChar ** means pointer to a pointer to an xmlChar (the first char in the first CString).

dbarker
+1  A: 

The accepted answers explanation is correct, but it's helpful to view some example code too. Here is just one way to extract the value from the attributes, at least it works when I tested it. I'm far from being a C guru though.

for (int i = 0; i < nb_attributes; i++) {
    const char *attr = (const char *)attributes[i];
    const char *begin = (const char *)attributes[i + 3];
    const char *end = (const char *)attributes[i + 4];
    int vlen = strlen(begin) - strlen(end);
    char val[vlen + 1];
    strncpy(val, begin, vlen);
    val[vlen] = '\0';
    NSLog(@"attribute %s: %d = %s", attr, i, val);
}

NSXMLParser is nice, but from what I can tell, it downloads the entire XML before processing. Using libxml it can read in chunks at a time. It allows greater flexibility, but higher learning curve.

b3b0p
A: 
gumbypp
A: 

5 items for each attribute, does it hold true for all Parsing or XML? where does #5 come from? thank you,

wannamobiles