Cocoa/Obj-C - Count elements in XML file - objective-c

Got an application wich read some infos in a specific XML file.
I would like to add one more option, here is the description:
Here is an example of my XML type file:
<?xml version="1.0" ?>
<Report>
<ReportCreationDate>20110307162840</ReportCreationDate>
<ReportTitle>Title sample</ReportTitle>
<ReportDescription>Description sample</ReportDescription>
<Videos>
<Video>
<VideoTitle>Video 1</VideoTitle>
<VideoDescription>Video description sample</VideoDescription>
</Video>
<Video>
<VideoTitle>Video 2</VideoTitle>
<VideoDescription>Video description sample</VideoDescription>
</Video>
</Videos>
</Report>
I would like to count how many <Video> elements is there in the <Videos> node.
For this example it's 2
How can I do that? Using NSXMLDocument?
I would like to write the result in a TextField.
Example: XML file contains 5 videos.
If someone can help, it would be great!
Thanks in advance
Miskia
(and sorry for my poor english, i'm a frenchy :p)

Something like this should get the job done. I'll leave the error handling up to you.
NSData *data = [NSData dataWithContentsOfFile:pathToYourXMLFile];
NSError *error = nil;
NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:0 error:&error];
if(!error)
{
NSXMLElement *rootElement = [document rootElement];
NSUInteger count = [[rootElement nodesForXPath:#"//Video" error:&error] count];
if(!error)
{
NSString *videosCount = nil;
if(count == 0)
videosCount = [NSString stringWithFormat:#"XML file contains no videos."];
else if(count == 1)
videosCount = [NSString stringWithFormat:#"XML file contains 1 video."];
else
videosCount = [NSString stringWithFormat:#"XML file contains %d videos.", count];
[myTextField setStringValue:videosCount];
}
}
[document release]

Related

How to find attribute of root node in touch xml?

NSString *xml = #"<?xml version="1.0" encoding="ISO-8859-15"?>
<ServerDateTime DateRequested="" DateSent="20141013_114855">
<DateTime>20141013_114857</DateTime>
</ServerDateTime>";
In above xml, how to find the attribute value of 'DataSent'?
I have tried by following, but i didn't get the value.
CXMLDocument *documentParser = [[CXMLDocument alloc]initWithData:[xml dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
NSArray *arrayResult = [documentParser nodesForXPath:#"//ServerDateTime" error:nil];
for(CXMLElement *element in arrayResult){
NSString *value = [element name];
if ([value isEqualToString:#"ServerDateTime"]) {
NSString *newLastSyncDate = [[element attributeForName:#"DataSent"] stringValue]; //it gives nil..
}
}
You may want to use XPath-queries to search for elements inside a XML. You have to look up if CXML supports this.
Maybe also take a look at this question.
There someone is searching for a given attribute with an XPath-query.

Email contents of UITableView

Is there any way to copy the contents of UITableView?
This is keeping in mind the fact that it IS in the same view as the table, however, the cells are custom (however only with 4 rows of text, each containing 4 labels).
Ideally I would like an excel like table to be sent via email, but I would like to find a way to copy each cell (and keep the formatting between them) and send it in a useful manner. (The information is feedback, with several categories rated out of 5!)
There's no API for doing that, you'll have to generate yourself the file with the content of the table. Just go one by one on the table.
You can save it as a CSV using this code (am looking now)
+ (NSString *)returnCreatedCSVFileNameFromColumns:(NSArray *)columns andDetails:(NSArray *)details {
//Create CSV string:
__block NSString *csvString = [[NSString alloc] init];
NSMutableArray *detailsArray = [[NSMutableArray alloc] init];
NSMutableArray *columnsArray = [[NSMutableArray alloc] init];
//Current Column:
for (TableColumn *column in columns) {
//Add the title for the column we are looping through:
csvString = [csvString stringByAppendingString:[NSString stringWithFormat:#"%#,", column.displayName]];
}
[columnsArray addObject:csvString];
csvString = #"";
for(Individual *individual in details)
{
for (TableColumn *tableColumn in columns)
{
csvString = [csvString stringByAppendingString:[NSString stringWithFormat:#"%#", [[tableColumn columnValueForIndividual:individual] stringByReplacingOccurrencesOfString:#"," withString:#"/"] ? : #""]];
}
csvString = [csvString stringByAppendingString:#","];
}
csvString = [csvString stringByAppendingString:#"\n"];
}
[detailsArray addObject:csvString];
csvString = #"";
csvString = [csvString stringByAppendingString:[NSString stringWithFormat:#"%#\n%#", [columnsArray objectAtIndex:0], [detailsArray objectAtIndex:0]]];
//Save csv to docs folder:
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [path objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"current_share.csv"];
NSData *csvFileData = [csvString dataUsingEncoding:NSASCIIStringEncoding];
if ([csvFileData writeToFile:filePath atomically:YES]) {
DLog(#"File written to: %#", filePath);
}
else
DLog(#"Saving file error");
return filePath;
}
I've used this to create a CSV file and return the local file path which can then be emailed somewhere. Obviously your data structure may be different but you should be able to get the general idea ... ? If not please let me know (I have omitted some code).

NSString writeToFile, NSSavePanel and write permissions

I've only been leaning Cocoa/Objective C for a few days so apologies that this is probably simple/obvious but it's got me stumped.
I've written this handler for saving 3 floats to a text file. However when I'm running it the files are not being saved. Could anyone suggest if there's an error in my code or if you think there's something else (like file write permissions) preventing the file from being written.
Research has lead me to look into Sandboxing, but that gets confusing very quickly and I'm hoping just running the app from xcode in debug would let me write to my user directory.
Heres the code:
- (IBAction)saveResultsAction:(id)sender {
//Sets up the data to save
NSString *saveLens = [NSString stringWithFormat:#"Screen width is %.02f \n Screen Height is %.02f \n Lens is %.02f:1",
self.myLens.screenWidth,
self.myLens.screenHeight,
self.myLens.lensRatio];
NSSavePanel *save = [NSSavePanel savePanel];
long int result = [save runModal];
if (result == NSOKButton) {
NSURL *selectedFile = [save URL];
NSLog(#"Save URL is %#", selectedFile);
NSString *fileName = [[NSString alloc] initWithFormat:#"%#.txt", selectedFile];
NSLog(#"Appended URL is %#", fileName);
[saveLens writeToFile:fileName
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
}
}
a NSURL object is no POSIX path..
its a URL and getting its description doesnt make it a path
NSString *fileName = [selectedFile.path stringByAppendingPathExtension:#"txt"];
BUT as said, you shouldnt have to append the .txt at all. just use what the panel returns. Else, there would be sandboxd errors because you dont have access rights to the modified filename :)
NSString *fileName = selectedFile.path;
The problem is that you don't need to append the file extension to the URL.The extension is already there.You could directly do this:
if (result == NSOKButton)
{
[saveLens writeToURL: [save URL]
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
}
I see you've already accepted an answer, but it may also be helpful to know how to debug this type of issue using NSError pointers.
Cocoa uses NSError with method calls which generate error conditions, which richly encapsulate errors. (Objective-C also has exceptions, but they're reserved for cases of programmer error, like an array index out of bounds, or a nil parameter that should never be.)
When you have a method which accepts an error pointer, usually it also return a BOOL indicating overall success or failure. Here's how to get more information:
NSError *error = nil;
if (![saveLens writeToFile:fileName
atomically:YES
encoding:NSUTF8StringEncoding
error:&error]) {
NSLog(#"error: %#", error);
}
Or even:
NSError *error = nil;
if (![saveLens writeToFile:fileName
atomically:YES
encoding:NSUTF8StringEncoding
error:&error]) {
[NSApp presentError:error];
}

Parsing a .csv file from a server with Objective-C

I have looked for an answer of a long time and still not found one so I thought I'd ask the question myself.
In my iPad app, I need to have the capability of parsing a .csv file in order to populate a table. I am using http://michael.stapelberg.de/cCSVParse to parse the csv files. However, I have only been successful in parsing local files. I have been trying to access a file from a server but am getting nowhere.
Here is my code to parse a local .csv file:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1)
{
//UITextField *reply = [alertView textFieldAtIndex:buttonIndex];
NSString *fileName = input.text;
NSLog(#"fileName %#", fileName);
CSVParser *parser = [CSVParser new];
if ([fileName length] != 0)
{
NSString *pathAsString = [[NSBundle mainBundle]pathForResource:fileName ofType:#"csv"];
NSLog(#"%#", pathAsString);
if (pathAsString != nil)
{
[parser openFile:pathAsString];
NSMutableArray *csvContent = [parser parseFile];
NSLog(#"%#", csvContent);
[parser closeFile];
NSMutableArray *heading = [csvContent objectAtIndex:0];
[csvContent removeObjectAtIndex:0];
NSLog(#"%#", heading);
AppDelegate *ap = [AppDelegate sharedAppDelegate];
NSManagedObjectContext *context = [ap managedObjectContext];
NSString *currentHeader = [heading objectAtIndex:0];
NSString *currentValueInfo = [heading objectAtIndex:1];
NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:#"Field" inManagedObjectContext:context];
[newObject setValue:#"MIS" forKey:#"header"];
[newObject setValue:currentHeader forKey:#"fieldName"];
for (NSArray *current in csvContent)
{
NSManagedObject *newField = [NSEntityDescription insertNewObjectForEntityForName:#"Field" inManagedObjectContext:context];
[newField setValue:currentHeader forKey:#"header"];
[newField setValue:currentValueInfo forKey:#"valueInfo"];
NSLog(#"%#", [current objectAtIndex:0]);
[newField setValue:[current objectAtIndex:0] forKey:#"fieldName"];
[newField setValue:[NSNumber numberWithDouble:[[current objectAtIndex:1] doubleValue]] forKey:#"value"];
}
NSError *error;
if (![context save:&error])
{
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
[self storeArray];
[self.tableView reloadData];
}
}
}
input.text = nil;
}
Forgive the weird beginning and ending brace indentation. :/
Anyway, so that is my code to take input from a user and access a file locally which I'm sure you guys have realized already. Now I want to know how to get the path of a file in my server.
Also if you guys see anything else wrong such as writing style and other bad habits please tell me as I'm new to iOS.
Thank you so much in advance! If you didn't understand my question please clarify as I'm bad at explaining myself at times! :)
As I am guessing you are trying to get data from a server's .csv file and want to show that data in table view list.
so I suggest you try to get that .csv file data in NSData and then work on that.
NSData *responseData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"serverUrl"]];
NSString *csvResponseString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
NSLog(#"responseString--->%#",csvResponseString);
Now try to use nsstring's method (componentsSeparatedByString) with coma (')
arrSepratedData = [[responseString componentsSeparatedByString:#","];
Now use this arr for UITableView data populate.

XML data crashing on numbers

hi friends
this is my xml file
1)
<sDescrizione>Crociera nei fiordi</sDescrizione>
2)
<sDescrizione>Fiat 500</sDescrizione>
3)
<sDescrizione>Orologio donna Glam sport Tissot</sDescrizione>
4)
<sDescrizione>Buoni La Rinascente 1000€</sDescrizione>
5)
<sDescrizione>Buoni Unieuro 1000€</sDescrizione>
this is what i want to retrieve from that xml file using CXML parsing method
the first 3 title are successfully retrived but when it comes at 4th it gives me error in my console like this
Entity: line 80: parser error : Input is not proper UTF-8, indicate encoding !
Bytes: 0x80 0x5D 0x5D 0x3E
<sDescrizione><![CDATA[Buoni La Rinascente 1000\200]]></sDescrizione>
^
this is my retrieving code:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CXMLDocument *xmlParser = [[[CXMLDocument alloc] initWithData:data options:0 error:nil] autorelease];
NSArray *resultNodes = [xmlParser nodesForXPath:#"//premio" error:nil];
for (CXMLElement *resultElement in resultNodes) {
for (int j=0; j<[resultElement childCount]; j++) {
NSString *tagName = [NSString stringWithString:[[resultElement childAtIndex:j] name]];
if ([tagName isEqualToString:#"sDescrizione"])
{
NSString *temp = [[resultElement childAtIndex:j] stringValue];
[catArray addObject:temp];
}
else if([tagName isEqualToString:#"idPremioSodexho"])
{
NSString *trmp = [[resultElement childAtIndex:j] stringValue];
}
}
}
Looks like the XML file is not in UTF-8 and CXMLDocument is assuming it is. When it hits the € sign its crashing. Set the correct encoding in the header of the XML file. If the XML file is encoded with ISO 8859-1 then set the header like:
<?xml version="1.0" encoding="ISO-8859-1"?>
This will allow CXMLDocument to correctly interpret the characters/codepage of your XML document.
I think you are facing the problem due to invalid Character-sets.
See, here is the line of code that you have used.
// when you don't specify any encoding, TouchXML will use NSUTF8Encoding as default
// which may lead to some problems
CXMLDocument *xmlParser = [[[CXMLDocument alloc] initWithData:data options:0 error:nil] autorelease];
// So, I recommend you to use following line of code, which may not lead you with messy situations.
CXMLDocument *doc = [[CXMLDocument alloc] initWithData:[request responseData] encoding:[request responseEncoding] options:0 error:nil];