I'm using Hpple to parse HTML and it seems that it doesn't recognize it is actually XML, which it should (XCode debugger shows this variable isXML = (BOOL) NO and it doesn't collect any data). How do I fix this?
This is my code (they're may be other bugs as well). the parse method/function is called first with [ListParser parse:#"http://www.fanfiction.net/book/Harry-Potter/" at:#"//div[#=\"class\"]"];:
#interface ListParser () //private
+ (NSArray*) getNodeListAt: (NSURL*) page inside: (NSString*) page;
+ (NSDictionary*) getNodeData: (TFHppleElement*) node;
+ (void) addMiniListData: (NSString*) list to: (NSMutableDictionary*) dict;
#end
#implementation ListParser
+ (NSArray*) getNodeListAt: (NSURL*) page inside: (NSString*) path { // "//div[#class"z-list"]"
NSData *data = [NSData dataWithContentsOfURL: page];
TFHpple *listparser = [TFHpple hppleWithHTMLData:data]; //WHERE CODE SEEMS TO STOP TO WORK
NSArray *done = [listparser searchWithXPathQuery: path];
return done;
}
+ (void) addMiniListData: (NSString*) list to: (NSMutableDictionary*) dict{
NSArray *parts = [list componentsSeparatedByString:#" - "];
for(NSString* p in parts){
NSArray* two = [p componentsSeparatedByString:#": "];
[dict setObject:[two objectAtIndex:1] forKey:[two objectAtIndex:0]];
}
}
+ (NSDictionary*) getNodeData: (TFHppleElement*) node{
NSMutableDictionary* data = [NSMutableDictionary dictionary];
[data setObject:[[[node firstChild] firstChild] objectForKey:#"href"] forKey:#"Image"];
[data setObject:[[node firstChild] text] forKey:#"Title"];
[data setObject:[[[[node firstChild] children] objectAtIndex:2] text] forKey:#"By"];
[data setObject:[[[[node firstChild] childrenWithClassName:#"z-indent"] objectAtIndex:0] text] forKey:#"Summery"];
[self addMiniListData:[[[[[[node firstChild] childrenWithClassName:#"z-indent"] objectAtIndex:0] childrenWithClassName:#"z-padtop2"] objectAtIndex:0] text] to: data];
return data;
}
+(NSArray*) parse: (NSString*) address at: (NSString*) path{
NSURL *url = [[NSURL alloc] initWithString:address];
NSArray* list = [self getNodeListAt:url inside:path];
NSMutableArray *data = [[NSMutableArray alloc] init];
for (TFHppleElement* e in list) {
[data addObject:[self getNodeData:e]];
}
return [[NSArray alloc] initWithArray: data];
}
#end
Here's a link to the tutorials I was following: http://www.raywenderlich.com/14172/how-to-parse-html-on-ios
If you need to parse XML with a TFHpple, you should tell it that you're doing so. You're calling +hppleWithHTMLData:. If you read the implementation of this method, you will see that it sets isXML to NO. Instead, use the hppleWithXMLData: method.
Related
I want to add string value dynamically from result.text and I wanted to display it in this way [#"17052648287",#"17052607335"] without losing the value. How can I do it?
NSMutableArray *strings = [#[#"17052648287",#"17052607335"] mutableCopy];
Add on coding
- (void)captureResult:(ZXCapture *)capture result:(ZXResult *)result{
if (!result) return;
if(self.hasScannedResult == NO)
{
//Scan Result, added into array
NSString *scanPackage = [NSString stringWithFormat:#"%#", result.text];
scanLists = [NSMutableArray new];
[scanLists addObject:scanPackage];
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSMutableArray *strings = [[NSMutableArray alloc]init];
strings = [#[result.text] mutableCopy];
[preferences setObject:strings forKey:#"strings"];
NSMutableArray *stringsArray = [preferences objectForKey:#"strings"];
for (NSString *string in stringsArray) {
NSLog(#"string: %#", string);
}
Declare an results array:
NSMutableArray * array = [NSMutableArray new];
Write below code where you get your result.text:
NSString *scanPackage = [NSString stringWithFormat:#"%#", result.text]; // If this code is working for you
[array addObject: scanPackage];
NSString *combined = [array componentsJoinedByString:#","];
NSLog(#"combined: %#", combined);
Is it possible to get only all the text content of the child elements recursively in hpple. Any method in TFHppleElement class?
such as the javascript
document.getElementById("testdiv").textContent
I'm using this code to get all content of the news title
NSURL *newURL = [NSURL URLWithString:#"http://somesite"];
NSData *newsData = [NSData dataWithContentsOfURL: newURL];
TFHpple *newsParser = [TFHpple hppleWithHTMLData: newsData];
NSString *newsXpathQueryString = #"//div[#class='item column-1']";
NSArray *newsNodes = [newsParser searchWithXPathQuery: newsXpathQueryString];
NSMutableArray *newNews = [[NSMutableArray alloc] initWithCapacity: 0];
for (TFHppleElement *element in newsNodes)
{
News *news = [[News alloc] init];
[newNews addObject: news];
news.title = [[element content] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
news.photo_url = [element objectForKey:#"src"];
_allNews = newNews;
[self.tableView reloadData];
}
}
you can use
news.title = [[element firstChild]content] to get children elements content
I wanted something like this - a quick boiler plate code, it is not an elegant solution with static contents. Please let me know, how can this be improved :)
#pragma mark - Hpple XML parser
/* The documents contents lots of nested div, table, span, style etc. */
- (NSString *) extractDefinition
{
NSString *html = [self.webView stringByEvaluatingJavaScriptFromString: #"document.getElementById('innerframe').innerHTML"];
if ([Resources stringIsEmpty:html]) {
return nil;
}
return [self extractSubDiv:html];
}
- (NSString *)extractSubDiv:(NSString *)html
{
TFHpple *hppleParser = [TFHpple hppleWithHTMLData:[html dataUsingEncoding:NSUTF8StringEncoding]];
NSString * xpathQuery;
xpathQuery = #"//div[#id='columnboth']";
NSArray * defNodes = [hppleParser searchWithXPathQuery:xpathQuery];
NSString * text = nil;
if ([defNodes count] > 0) {
TFHppleElement * element = [defNodes objectAtIndex:0];
text = [self parseContents:element];
} else {
xpathQuery = #"//div[#id='columnsingle']";
defNodes = [hppleParser searchWithXPathQuery:xpathQuery];
if ([defNodes count] > 0) {
TFHppleElement * element = [defNodes objectAtIndex:0];
text = [self parseContents:element];
}
}
return text;
}
- (NSString *) parseContents:(TFHppleElement *)element {
NSArray * innhold = [element searchWithXPathQuery:#"//div[contains(#class,'articlecontents')]"];
return [self getTextFromArray:innhold];
}
static NSMutableString * contents;
- (NSString *) getTextFromArray:(NSArray *)hppleElments {
NSMutableString * text = [[NSMutableString new] autorelease];
contents = nil;
contents = [[NSMutableString new] autorelease];
for (TFHppleElement * e in hppleElments) {
[text appendFormat:#"%# ", [self getText:e]];
}
return text;
}
/* Here are more nested div and then span for text. */
- (NSString *) getText:(TFHppleElement *)element
{
if ([element isTextNode]) {
[contents appendFormat:#" %#", element.content];
}
for (TFHppleElement * e in element.children) {
[self getText:e];
}
return contents;
}
I've been trying to convert a Quiz app that was based for Mac OS to iOS because I liked the idea of loading all questions from a single .txt file.
I'm still pretty new in the Objective-C language as I've used C# before.
The questions and answers are loaded via this function:
- (void)loadQuestionsAndAnswersArray {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Quiz1" ofType:#"txt"];
NSString *textFileString = [NSString stringWithContentsOfFile:filePath encoding:NSStringEncodingConversionAllowLossy error:NULL];
NSArray *seperatedQA = [textFileString componentsSeparatedByString:#"\n\n"];
for (NSString *QA in seperatedQA) {
NSString *questionString = [[[QA componentsSeparatedByString:#"\n"] objectAtIndex:0] stringByReplacingOccurrencesOfString:#"Q:" withString:#""];
NSMutableArray *answers = [[QA componentsSeparatedByString:#"A:"] mutableCopy];
[answers removeObjectAtIndex:0];
int correctAnswerLoc = 0;
for (int i = 0; i < answers.count; i++) {
NSString *answer = [answers objectAtIndex:i];
NSString *editedAnswer = [answer stringByReplacingOccurrencesOfString:#"\n" withString:#""];
editedAnswer = [editedAnswer stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[answers removeObjectAtIndex:i];
[answers insertObject:editedAnswer atIndex:i];
answer = editedAnswer;
if ([answer rangeOfString:#"[CORRECT]"].location != NSNotFound) {
correctAnswerLoc = [answers indexOfObject:answer];
NSString *editedAnswer = [answer stringByReplacingOccurrencesOfString:#"[CORRECT]" withString:#""];
[answers removeObjectAtIndex:i];
[answers insertObject:editedAnswer atIndex:i];
}
}
NSLog(#"answers = %#", answers);
NSDictionary *QADictionary = [NSDictionary dictionaryWithObjectsAndKeys:questionString, #"question", answers, #"answers", [NSNumber numberWithInt:correctAnswerLoc], #"correctAnswerLocation", nil];
[questionsAndAnswers addObject:QADictionary];
}
resultsArray = [[NSMutableArray alloc] initWithCapacity:[questionsAndAnswers count]];
}
The app then has a text field for the question and then 3 buttons, one for each answer. And when a new questions appears it changes the text within the text field and the title of the buttons.
This code works like a charm on the Mac App but on the iOS version it's like it can't find the txt file, the buttons etc is left blank.
I've been sitting on this for a week or so about now and that's the reason for this post.
The iOS app is based on this Github Mac app: https://github.com/SquaredTiki/Quizzer
If you want to have a look at how I've tried to convert the app here's a link to that to:
https://www.dropbox.com/s/1jqz9ue97p3v2h1/iTrafikk.zip?dl=0
And of course I'm not asking you to solve the whole issue for me, maybe just push me in the right direction if possible :)
I checked your project. And it seems that you are not calling loadQuestionsAndAnswersArray anywhere in your code. Also you are not initializing the questionsAndAnswers anywhere in the project.
Change your code like:
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadQuestionsAndAnswersArray];
}
- (void)loadQuestionsAndAnswersArray
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Quiz1" ofType:#"txt"];
NSString *textFileString = [NSString stringWithContentsOfFile:filePath encoding:NSStringEncodingConversionAllowLossy error:NULL];
NSArray *seperatedQA = [textFileString componentsSeparatedByString:#"\n\n"];
for (NSString *QA in seperatedQA)
{
NSString *questionString = [[[QA componentsSeparatedByString:#"\n"] objectAtIndex:0] stringByReplacingOccurrencesOfString:#"Q:" withString:#""];
NSMutableArray *answers = [[QA componentsSeparatedByString:#"A:"] mutableCopy];
[answers removeObjectAtIndex:0];
int correctAnswerLoc = 0;
for (int i = 0; i < answers.count; i++)
{
NSString *answer = [answers objectAtIndex:i];
NSString *editedAnswer = [answer stringByReplacingOccurrencesOfString:#"\n" withString:#""];
editedAnswer = [editedAnswer stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[answers removeObjectAtIndex:i];
[answers insertObject:editedAnswer atIndex:i];
answer = editedAnswer;
if ([answer rangeOfString:#"[CORRECT]"].location != NSNotFound)
{
correctAnswerLoc = [answers indexOfObject:answer];
NSString *editedAnswer = [answer stringByReplacingOccurrencesOfString:#"[CORRECT]" withString:#""];
[answers removeObjectAtIndex:i];
[answers insertObject:editedAnswer atIndex:i];
}
}
NSLog(#"answers = %#", answers);
NSDictionary *QADictionary = [NSDictionary dictionaryWithObjectsAndKeys:questionString, #"question", answers, #"answers", [NSNumber numberWithInt:correctAnswerLoc], #"correctAnswerLocation", nil];
if (!questionsAndAnswers)
{
questionsAndAnswers = [[NSMutableArray alloc] init];
}
[questionsAndAnswers addObject:QADictionary];
}
resultsArray = [[NSMutableArray alloc] initWithCapacity:[questionsAndAnswers count]];
[self loadQuestionsAndAnswersIntoInterface];
}
I am trying to pull an LDAP "jpegPhoto" attribute from an openLDAP server using a iOS openLDAP framework. The framework pulls the data as a dictionary of NSStrings.
I need to convert the NSString of "jpegPhoto" (which also appears to be base64 encoded) to UIImage, with the end result being that I display the jpegPhoto as the user's image when they login.
More Info:
-(NSDictionary *)doQuery:(NSString *)query:(NSArray *)attrsToReturn {
...
while(attribute){
if ((vals = ldap_get_values_len(ld, entry, attribute))){
for(int i = 0; vals[i]; i++){
//Uncomment if you want to see all the values.
//NSLog(#"%s: %s", attribute, vals[i]->bv_val);
if ([resultSet objectForKey:[NSString stringWithFormat:#"%s",attribute]] == nil){
[resultSet setObject:[NSArray arrayWithObject:[NSString stringWithFormat:#"%s",vals[i]->bv_val]] forKey:[NSString stringWithFormat:#"%s",attribute]];
}else{
NSMutableArray *array = [[resultSet objectForKey:[NSString stringWithFormat:#"%s",attribute]] mutableCopy];
[array addObject:[NSString stringWithFormat:#"%s",vals[i]->bv_val]];
[resultSet setObject:array forKey:[NSString stringWithFormat:#"%s",attribute]];
}
}
ldap_value_free_len(vals);
};
ldap_memfree(attribute);
attribute = ldap_next_attribute(ld, entry, ber);
};
...
}
-(UIIMage *)getPhoto{
NSString *query = [NSString stringWithFormat:#"(uid=%#)",self.bindUsername];
NSArray *attrsToReturn = [NSArray arrayWithObjects:#"cn",#"jpegPhoto", nil];
NSDictionary *rs = [self doQuery:query:attrsToReturn];
NSString *photoString = [[rs objectForKey:#"jpegPhoto"] objectAtIndex:0];
NSLog(#"The photoString is: %i %#",[photoString length],#"characters long"); //returns 4
NSData *photoData = [NSData dataWithBase64EncodedString:photoString];
UIImage *userPhoto = [UIImage imageWithData:photoData];
return userPhoto;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.studentNameLabel.text = [NSString stringWithFormat:#"Hi %#!",[self.ldap getFullName]];
self.studentPhotoImage.image = [self.ldap getPhoto];
[self checkForProctor];
}
Try this code
NSData *dataObj = [NSData dataWithBase64EncodedString:beforeStringImage];
UIImage *beforeImage = [UIImage imageWithData:dataObj];
There are many similar questions in Stackoverflow.. Please refer the following links
UIImage to base64 String Encoding
UIImage from bytes held in NSString
(Since there has been no working code posted for getting the image data from LDAP, I wanted to add this answer for the benefit of future visitors.)
The missing piece was reading the binary data into an NSData object rather than an NSString when you have binary data that might contain NULL (zero) values within it, such as images or GUIDs.
value = [NSData dataWithBytes:vals[0]->bv_val length:vals[0]->bv_len];
+ (NSArray *)searchWithBaseDN:(const char *)baseDN andFilter:(const char *)filter andScope:(int)scope {
...
while(entry)
{
// create a dictionary to hold attributes
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
attribute = ldap_first_attribute(ld, entry, &ber);
while(attribute)
{
if ((vals = ldap_get_values_len(ld, entry, attribute)))
{
if (ldap_count_values_len(vals) > 1) {
NSMutableArray *values = [[NSMutableArray alloc] init];
for(int i = 0; vals[i]; i++) {
[values addObject:[NSString stringWithUTF8String:vals[i]->bv_val]];
}
[dictionary setObject:values forKey:[NSString stringWithUTF8String:attribute]];
} else {
NSObject *value = nil;
if (strcmp(attribute, "thumbnailPhoto") == 0 || strcmp(attribute, "objectGUID") == 0) {
value = [NSData dataWithBytes:vals[0]->bv_val length:vals[0]->bv_len];
} else {
value = [NSString stringWithFormat:#"%s", vals[0]->bv_val];
}
[dictionary setObject:value forKey:[NSString stringWithUTF8String:attribute]];
}
ldap_value_free_len(vals);
};
ldap_memfree(attribute);
attribute = ldap_next_attribute(ld, entry, ber);
};
...
}
Once more I come to the Internet, hat in hand. :)
I'm attempting to use a class method to return a populated array containing other arrays as elements:
.h:
#interface NetworkData : NSObject {
}
+(NSString*) getCachePath:(NSString*) filename;
+(void) writeToFile:(NSString*)text withFilename:(NSString*) filePath;
+(NSString*) readFromFile:(NSString*) filePath;
+(void) loadParkData:(NSString*) filename;
+(NSArray*) generateColumnArray:(int) column type:(NSString*) type filename:(NSString*) filename;
#end
.m:
#import "NetworkData.h"
#import "JSON.h"
#import "Utility.h"
#implementation NetworkData
+(NSString*) getCachePath:(NSString*) filename {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *cachePath = [NSString stringWithFormat:#"%#/%#", [paths objectAtIndex:0], filename];
[paths release];
return cachePath;
}
+(void) writeToFile:(NSString*)text withFilename:(NSString*) filename {
NSMutableArray *array = [[NSArray alloc] init];
[array addObject:text];
[array writeToFile:filename atomically:YES];
[array release];
}
+(NSString*) readFromFile:(NSString*) filename {
NSFileManager* filemgr = [[NSFileManager alloc] init];
NSData* buffer = [filemgr contentsAtPath:filename];
NSString* data = [[NSString alloc] initWithData:buffer encoding:NSUTF8StringEncoding];
[buffer release];
[filemgr release];
return data;
}
+(void) loadParkData:(NSString*) filename {
NSString *filePath = [self getCachePath:filename];
NSURL *url = [NSURL URLWithString:#"http://my.appserver.com"];
NSData *urlData = [NSData dataWithContentsOfURL:url];
[urlData writeToFile:filePath atomically:YES];
}
+(NSArray*) generateColumnArray:(int) column type:(NSString*) type filename:(NSString*) filename {
// NSLog(#"generateColumnArray called: %u %# %#", column, type, filename);
// productArray = [[NSMutableArray alloc] init];
// NSString *filePath = [self getCachePath:filename];
// NSString *fileContent = [self readFromFile:filePath];
// NSString *jsonString = [[NSString alloc] initWithString:fileContent];
// NSDictionary *results = [jsonString JSONValue];
// NSArray *eventsArray = [results objectForKey:type];
// NSInteger* eventsArrayCount = [eventsArray count];
// NSInteger* a;
// for (a = 0; a < eventsArrayCount; a++) {
// NSArray *eventsColSrc = [eventsArray objectAtIndex:a];
// NSArray *blockArray = [eventsColSrc objectAtIndex:column];
// [productArray addObject:blockArray];
// [blockArray release];
// }
// [eventsArray release];
// [results release];
// [jsonString release];
// [fileContent release];
// [filePath release];
// [a release];
// [eventsArrayCount release];
// return productArray;
}
-(void)dealloc {
[super dealloc];
}
#end
.. and the call:
NSArray* dataColumn = [NetworkData generateColumnArray:0 type:#"eventtype_a" filename:#"data.json"];
The code within the method works (isn't pretty, I know - noob at work). It's essentially moot because just calling it (with no active code, as shown) causes the app to quit before the splash screen reveals anything else.
I'm betting this is a headslapper - many thanks for any knowledge you can drop.
If your app crashes, there's very likely a message in the console that tells you why. It's always helpful to include that message when seeking help.
One obvious problem is that your +generateColumnArray... method is supposed to return a pointer to an NSArray, but with all the code in the method commented out, it's not returning anything, and who-knows-what is being assigned to dataColumn. Try just adding a return nil; to the end of the method and see if that fixes the crash. Again, though, look at the error message to see specifically why the code is crashing, and that will lead you to the solution.
Well, you're not returning a valid value from your commented out code. What do you use 'dataColumn' for next? Running under the debugger should point you right to the issue, no?