I want to write a not so complicated but large file within my app and be able to send it by mail (using MFMailComposeViewController)
Since NSXMLElement and related classes are not ported to iPhone SDK what options do I have for creating XML documents?
Thanks in advance.
I would recommend using KissXML. The author started in a similar situation as you and created an NSXML compatible API wrapper around libxml. He discusses the options and decisions here on his blog.
Shameless self-promotion: KSXMLWriter
Try the open source XML stream writer for iOS:
Written in Objective-C, a single .h. and .m file
One #protocol for namespace support and one for without
Example:
// allocate serializer
XMLWriter* xmlWriter = [[XMLWriter alloc]init];
// start writing XML elements
[xmlWriter writeStartElement:#"Root"];
[xmlWriter writeCharacters:#"Text content for root element"];
[xmlWriter writeEndElement];
// get the resulting XML string
NSString* xml = [xmlWriter toString];
This produces the following XML string:
<Root>Text content for root element</Root>
It's a homework exercise in NSString building. Abstractly, create a protocol like:
#protocol XmlExport
-(NSString*)xmlElementName;
-(NSString*)xmlElementData;
-(NSDictionary*)xmlAttributes;
-(NSString*)toXML;
-(NSArray*)xmlSubElements;
#end
Make sure everything you're saving implements it and build the XML with something like the following:
-(NSString*)toXML {
NSMutableString *xmlString;
NSString *returnString;
/* Opening tag */
xmlString = [[NSMutableString alloc] initWithFormat:#"<%#", [self xmlElementName]];
for (NSString *type in [self xmlAttributes]) {
[xmlString appendFormat:#" %#=\"%#\"", type, [[self xmlAttributes] valueForKey:type]];
}
[xmlString appendString:#">"];
/* Add subelements */
for (id<XmlExport> *s in [self xmlSubElements]) {
[xmlString appendString:[s toXML]];
}
/* Data */
if ([self xmlElementData]) {
[xmlString appendString:[self xmlElementData]];
}
/* Close it up */
[xmlString appendFormat:#"</%#>", [self xmlElementName]];
/* Return immutable, free temp memory */
returnString = [NSString stringWithString:xmlString];
[xmlString release]; xmlString = nil;
return returnString;
}
Related
I am using the hpple parsing library in my iOS app. https://github.com/topfunky/hpple
I am having a problem where the parser is not correctly parsing my HTML page on iOS 7. It is correctly parsed on iOS 6 and I get the returned objects. On iOS 7 I am returned only a partial parse and a wrong one. The library incorrectly reads the HTML on iOS 7. I have tried debugging the problem and have concluded that the problem lies in this method.
NSArray *PerformXPathQuery(xmlDocPtr doc, NSString *query)
{
xmlXPathContextPtr xpathCtx;
xmlXPathObjectPtr xpathObj;
/* Create xpath evaluation context */
xpathCtx = xmlXPathNewContext(doc);
if(xpathCtx == NULL)
{
//NSLog(#"Unable to create XPath context.");
return nil;
}
/* Evaluate xpath expression */
xpathObj = xmlXPathEvalExpression((xmlChar *)[query cStringUsingEncoding:NSUTF8StringEncoding], xpathCtx);
if(xpathObj == NULL) {
//NSLog(#"Unable to evaluate XPath.");
xmlXPathFreeContext(xpathCtx);
return nil;
}
xmlNodeSetPtr nodes = xpathObj->nodesetval;
if (!nodes)
{
//NSLog(#"Nodes was nil.");
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
return nil;
}
NSMutableArray *resultNodes = [NSMutableArray array];
for (NSInteger i = 0; i < nodes->nodeNr; i++)
{
NSDictionary *nodeDictionary = DictionaryForNode(nodes->nodeTab[i], nil,false);
if (nodeDictionary)
{
[resultNodes addObject:nodeDictionary];
}
}
/* Cleanup */
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
return resultNodes;
}
doc and query are not nil when this method is called. I don't know how to log the other classes in this method and I do not know which one of them returns the wrong parse. Maybe the problem lies in Apple's libxml2.dylib
I do not know exactly what the code does in the method until the end of the method. Any help will be greatly appreciated as I am stumped why the library cannot parse on iOS 7 the same HTML page it can parse on iOS 6.
On iOS 7 this library correctly parses other HTML pages in other apps. The code I use to parse initiate the parser is
NSData *htmlData = [NSData dataWithContentsOfURL:dataURL];
TFHpple *dataParser = [TFHpple hppleWithHTMLData:htmlData];
NSString *dataXpathQueryString = #"//td[3]";
NSArray *dataNodes = [dataParser searchWithXPathQuery:dataXpathQueryString];
NSMutableArray *newData = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in dataNodes) {
data = [[GFCData alloc] init];
[newData addObject:data];
data.title = [[element firstChild] content];
data.title = [data.title stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
where dataURL is http://www.gfchurch.com/ru/pages/video.php?a=5&b=51&c=37
I can provide more information if needed.
This problem was solved by not using // in the searchPathQuery. I should test this problem again because the library was updated a month ago and maybe the issue was solved.
I solved this issue by correctly managing namespaces via #xmlXPathRegisterNs#
I was not having the issue parsing HTML, but SOAP response.
I am trying to create a http body that I am going to pass in using NSURLRequest post.
I have my connection class all set up.
The thing is i have several methods that return NSStrings and UInt32's and one construction method that I want to use to put all of these methods into one http body which will be of data type format.
However I'm not sure how to call these methods that return the correct data from my construction method to gather the data into one data object.
here is some code that I have (shortened so its a little clearer)
these methods are used to return the data needed
- (UInt32) addDataVer
{
UInt32 dataVer = 0;
return dataVer;
}
- (NSString *) addReg
{
NSString *reg = [[NSString alloc] initWithString:#"abcd1"];
return reg;
}
- (NSString *) addActiv
{
NSString *activ = [[NSString alloc] initWithString:#"abcd2"];
return activ;
}
from here I'm not sure what to do, or how to get the data. I have created a construction method, that I want to use to grab the data and then I want to use that data to build a NSData object where I put the returning data into it in order.
this is my construction class
- (void) constructRequest
{
//what the heck do I call in here? lol
}
the last thing I will have to do is figure out how to put the nsdata representation of each return value into the data object... if that makes sense...
any help would be appreciated.
UPDATE::
So I figured out how to get the return value into my construction method, by following the force!
- (void) constructRequest
{
NSString *mystring = [[NSString alloc] initWithString:[self addReg]];
NSLog(#"mystring %#", mystring);
}
however I am not sure how to do this with a returning UInt32, or how to convert this in to a NSData structure
From Apple Docs on String formatting https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265-SW1
[NSString stringWithFormat:"my unsigned 32-bit int: %d", [self addDataVer]];
from Apple Docs about NSString class
To convert your entire string to data:
[myNSString dataUsingEncoding:NSUTF8StringEncoding];
I have an app where I need to display a report of six (6) columns and a variable number of rows (lines).
What's the best approach to do this? I tried UITextView but there is no way to set the font size to make the data fit on each line.
I usually use UIWebViews for this.
A dozen lines to create a html table out of a NSArray and some nice CSS.
Edit: I can show you an example I wrote in the last 5 minutes. There's not much to it if you have a basic understanding of html. Of course you could take it much further than that.
And even if you don't know anything about html I'm sure you can learn the basics for some simple table display within a couple of hours.
- (NSString *)htmlTableRowFromArray:(NSArray *)array withOpenTag:(NSString *)openTag andCloseTag:(NSString *)closeTag {
NSMutableString *rowHtmlStr = [NSMutableString string];
for (NSString *str in array) {
[rowHtmlStr appendFormat:#"%# %# %#\n", openTag, str, closeTag];
}
return rowHtmlStr;
}
- (void)displayReport {
NSArray *dataArray = ...;
NSArray *myHeader = ...;
NSMutableString *htmlStr = [NSMutableString stringWithString:#"<html><head><title>MyTable</title></head><body>"];
// the table starts here
[htmlStr appendString:#"<table width=\"100%\" style=\"background-color:#CCC\" border=\"1\" cellpadding=\"4\" cellspacing=\"0\">"];
// this will create a table header
[htmlStr appendFormat:#"<tr>%#</tr>", [self htmlTableRowFromArray:myHeader withOpenTag:#"<th>" andCloseTag:#"</th>"]];
for (NSArray *line in dataArray) {
// one row for each array in the data
[htmlStr appendFormat:#"<tr>%#</tr>", [self htmlTableRowFromArray:line withOpenTag:#"<td>" andCloseTag:#"</td>"]];
}
// table ends
[htmlStr appendString:#"</table>"];
[htmlStr appendString:#"</body></html>"];
[webView loadHTMLString:htmlStr baseURL:nil];
}
the table should look like this. Imho not that bad for the amount of work.
what is the best way to load a xml file as an parsed object in objective c?
Check out TouchXML. It's the easiest library I've found so far. Supports xpath on a basic level. http://code.google.com/p/touchcode/wiki/TouchXML. Here's a sample (code removed) from a recent project:
CXMLDocument *parser = [[CXMLDocument alloc] initWithData:theData encoding:NSUTF8StringEncoding options:0 error:nil];
NSArray *nodes = [parser nodesForXPath:#"//item" error:nil];
for (CXMLElement *resultElement in nodes)
{
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithCapacity:0];
// Create a counter variable as type "int"
int counter;
// Loop through the children of the current node and add to dictionary
for (counter = 0; counter < [resultElement childCount]; counter++)
{
// Add each field to the dictionary with the node name as
// key and node value as the value
[data setObject:[[resultElement childAtIndex:counter] stringValue]
forKey:[[resultElement childAtIndex:counter] name]];
}
// do stuff with the dictionary by named keys
// ...
// release dict
[data release];
}
[parser release];
If you know some C, the best (i.e. fastest parsing, lowest memory footprint) way to parse XML into Objective-C objects is probably via the SAX event parser in libxml2. Apple has a sample project called XMLPerformance which demonstrates how this is done.
I recommend using TouchXML. Great documentation and relatively easy to use compared with the NSXMLParser.
Look into the NSXmlParser. It can read xml and gives you methods you can override to process the contents.
I am just learning objective-c and iPhone development, and I am really struggling with some very basic tasks. I am only on my 3rd day of the learning process - so that is to be expected somewhat. I'm still almost ashamed to ask such a simple question.
Anyhow, here's my question. I have a .NET web service which I call using a GET for http://somehost/ping
it returns 'pong'
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">pong</string>
The simplest of test cases.
Back on the iPhone when I retrieve the URL I have the above string as a result. I only want the 'pong' part. This seems like programming 101, but I can't seem to find a simple example of how to do it that doesn't involve defining delagates or other seemingly complex processing steps.
The problem is simple enough, find the first '>' and extract everything from there until the first '<' as an NSString. That's all I need to do.
Does anyone have a basic example of how to do this?
This is dry-coded, and kinda ugly imho. But here is a more direct answer.
NSString *xml = #"<tag>pong</tag>";
NSRange range = [xml rangeOfString:#">"];
xml = [xml substringFromIndex:range.location + 1];
range = [substring rangeOfString:#"<"];
xml = [xml substringToIndex:range.location];
Hey Sylvanaar, I'm having to do similar types of parsing inside of the client. My general methodolgy for parsing xml responses is like this. I'm pretty sure the classes are available on iphone side too. Note: it may not be the absolute best method, but it does work.
- (id)initWithXMLNode:(NSXMLNode *)node {
self = [super init];
if (self != nil) {
NSError *error;
NSArray *objects;
// Get the fingerprint
objects = [node objectsForXQuery:#"for $fingerprint in ./Fingerprint return data($fingerprint)" error:&error];
handleErrorInInit(error)
fingerprint = getFingerprint(objects);
// Get the moduleName
objects = [node objectsForXQuery:#"for $moduleName in ./Foldername return data($moduleName)" error:&error];
handleErrorInInit(error)
moduleName = getNSString(objects);
}
return self;
}
Worth showing this too. Note that NSXMLDocuments are a subclass of NSXMLNodes.
- (NSXMLDocument *)xmlDocumentFromData:(NSData *)data {
NSError *error;
NSXMLDocument *document = [[[NSXMLDocument alloc] initWithData:data options:0 error:&error] autorelease];
if (error) {
[NSApp presentError:error];
return nil;
}
return document;
}
Sometimes a full on XML parse makes sense, but a quick index/substring routine can be appropriate as well:
NSRange startBracket = [xmlFragment rangeOfString:#">"];
if(startBracket.location != NSNotFound) {
NSRange endBracket = [xmlFragment rangeOfString:#"<"
options:0
range:NSMakeRange(startBracket.location,
[xmlFragment length] - startBracket.location)];
if(endBracket.location != NSNotFound) {
NSString *value = [[xmlFragment substringFromIndex:startBracket.location+1]
substringToIndex:endBracket.location];
// Do something with value...
}
}
(Not tested, needs more error handling, yadda yadda yadda..)