iPhone parse xhtml + css - iphone

I have a complex long XHTML file, which contains CSS. Searching on google and on this site, I've found some libraries that can be useful on XHTML parsing:
NSXMLParser
TBXML
And some others
However, I'm wondering if there is any library for iPhone that can convert a xhtml + css document to a NSAttributedString (only the text, of course).
I have been thinking on that problem, and I have had some ideas, but I think it won't be very efficient. My main idea is formed by this steps:
Detect on the XTHML file all tags with an id or class attribute and get the range of the string where they have effect (I cannot achieve this).
Save all the CSS attributes on a NSDictionary, with more NSDictionary objects inside. Something like this:
mainDict {
object: dictionary {
object: #"#00ff00"
key: #"color"
object: #"1em"
key: #"font-size"
}
key: #"a id"
object: anotherDictionary {
...
}
key: #"another id"
}
Convert these CSS attributes dictionary on the NSAttributedStringattributes dictionary.
I know that this is complex, and I don't need you to provide the code (of course, if you provide it, it would be great), I only want the link to a library or, if it doesn't exist, some advice for create a parser myself.
Of course, if you need some more information, ask by comments.
Thanks you!!

It depends on your needs if this will do what you want, but DTCoreText has an HTML -> NSAttributedString converter. It's very specific for what DTCoreText wants to / needs to do, but it might at least point you in the right direction.

My way to parse an HTML string into NSAttributedString is to recursively append parsed node (and its childNodes) into an NSMutableAttributedString.
I am not ready to publish my full code anywhere yet. But hopefully this can give you some hints...
NSString+HTML.h
/* - toHTMLElements
* parse the string itself into a dictionary collection of htmlelements for following keys
* : #"attributedString" // html main body
* : #"insets" // images and/or videos with range info
* : #"as" // href with range info
*
*/
- (NSMutableDictionary*) toHTMLElements;
NSString+HTML.m
- (NSMutableDictionary*) toHTMLElements {
// …
// handle escape encoding here
// assume that NSString* htmlString is the processed string;
// …
NSMutableDictionary * htmlElements = [[NSMutableDictionary dictionary] retain];
NSMutableAttributedString * attributedString = [[[NSMutableAttributedString alloc] init] autorelease];
NSMutableArray * insets = [NSMutableArray array];
NSMutableArray * as = [NSMutableArray array];
[htmlElements setObject:attributedString forKey:HTML_ATTRIBUTEDSTRING];
[htmlElements setObject:insets forKey:HTML_INSETS];
[htmlElements setObject:as forKey:HTML_AS];
// parse the HTML with an XML parser
// CXXML is a variance of TBXML (http://www.tbxml.co.uk/ ) which can handle the inline tags such as <span>
// code not available to public yet, so write your own inline-tag-enabled HTML/XML parser.
CXXML * xml = [CXXML tbxmlWithXMLString:htmlString];
TBXMLElement * root = xml.rootXMLElement;
TBXMLElement * next = root->firstChild;
while (next != nil) {
//
// do something here for special treatments if needed
//
NSString * tagName = [CXXML elementName:next];
[self appendXMLElement:next withAttributes:[HTMLElementAttributes defaultAttributesFor:tagName] toHTMLElements:htmlElements];
next = next->nextSibling;
}
return [htmlElements autorelease];
}
- (void) appendXMLElement:(TBXMLElement*)aElement withAttributes:(NSDictionary*)parentAttributes toHTMLElements:(NSMutableDictionary*) htmlElements {
// do your parse of aElement and its attribute values,
// assume NSString * tagAttrString is the parsed html attribute string (either from "style" attribute or css file) for this tag like : width:200px; color:#123456;
// let an external HTMLElementAttributes class to handle the attribute updates from the parent node's attributes
NSDictionary * tagAttr = [HTMLElementAttributes updateAttributes: parentAttributes withCSSAttributes:tagAttrString];
// create your NSAttributedString styled by tagAttr
// create insets such as images / videos or hyper links objects
// then update the htmlElements for storage
// once this tag is handled, recursively visit and process the current tag's children
TBXMLElement * nextChild = aElement->firstChild;
while (nextChild != nil) {
[self appendXMLElement:nextChild withAttributes:tagAttr toHTMLElements:htmlElements];
nextChild = nextChild->nextSibling;
}
}

Related

How to convert text file to xml in xcode

I have a big text file that I want to convert into Xcode. I added the text file in the main bundle (drag and drop) into my project . I can see the text file viewDidLoad.
But I like to convert it to XML file. For instance my file looks like :
asasasasasas
wewewewewewe
qwqwqwqwqwqw
xyz_ 22 aaaaaaaaaaa
fgfgfgfgfgfgfg
ererererererer
abc_ 12 bbbbbbbbbb
jkjkjkjkjkjkjk
lalallalalalal
In the above mentioned, I want to eliminate the first 3 lines, to start from xyz_ 22 as (parent), jkjkjkj as child lalalala as a child.
I need only the idea how to implement this ... I'll write the code :)
mycode:
- (IBAction)readUsingObjectiveC:(id)pId {
NSString * zStr = [NSString stringWithContentsOfFile:#"/Users/dd007/Desktop/abc.txt" encoding:NSASCIIStringEncoding error:NULL];
NSLog(#"readUsingObjectiveC zStr=\n%#",zStr);
// now to extract the data line by line
NSArray * zAryOfLines = [zStr componentsSeparatedByString:#"\n"];
if([zAryOfLines count] == 0)
{
NSLog(#"readUsingObjectiveC zAryOfLines count = 0");
return;
}
//for (int i=0; i<([zAryOfLines count]-30); ++i)
for ( int i=30; i<zAryOfLines ; i++)
{
if([[zAryOfLines objectAtIndex:i] isEqualToString:#"xyz_ "])
{
NSLog(#"<msg1>%#<msg1/>\n",[zAryOfLines objectAtIndex:i]);
NSLog(#"<msg2>%#<msg2/>\n",[zAryOfLines objectAtIndex:i+1]);
NSLog(#"<msg3>%#<msg3/>\n",[zAryOfLines objectAtIndex:i+2]);
[zArrayOfLines writeToFIle:#"/.....documents/..save.xml" automatically:YES encodingNSASCIIStringEncoding error:NULL];
}
}
I am getting convert into xml format but i like to save the file in .xml .. but i am getting error could any one tell me where i am doing mistake ??????
You can't convert a file to xml using XCode, you have two options:
You can create a python or ruby script that parses your file and makes a XML file, and then use a xml parser,
Or you can create a class that parses your plain file with the rules you want.
I think you want to transform a .txt to a .xml so for make this i read the .txt to a nsstring i cut this into a NSArray with componentsSeparatedByString:#"\n" method of NSString (take care of \r character) and after just don't take line you don't want and create a new NSString for add tag XML to your line if you now where place the good tag or check it with the contains of your line in the NSArray for finish just saved in new file with extension .xml.
If you need help for write code like i describe say it.
There is no option for converting the text to xml directly in iOS.
But you can do it by passing the data manually.
You can use either libXml framework or GDataXml for doing this.
For libXml xml generation sample code go to this link and download Chapter 10.zip
Please check this tutorial for Read and Write XML Documents with GDataXML
Also TCMXMLWriter is an opensource xmlgenerator :
I think a code like this work for your example :
NSString *contentFile = [[NSString alloc] initWithContentsOfFile:pathFile encoding:NSUTF8StringEncoding error:nil];
NSArray *lineFile = [contentFile componentsSeparatedByString:#"\n"];
NSMutableString *xmlFile = [[NSMutableString alloc] init];
For(int i = 3; i < lineFile.count; i++)//i = 3 for don't take the 3 first line
{
if ([((NSString *)[lineFile objectAtIndex:i]) rangedOfString:#"test"].location != NSNotFound)
{
xmlFile = [NSString stringWithFormat:#"%#<nameTag>%#</nameTag>", xmlFile, (NSString *)[lineFile objectAtIndex:i]];
}
else if ...
}
And save he nsstring in new file. Possible in loop to make by number like if i is multiple of 3 of 4 etc...

Why does SBJson JSON parsing only get the last key of interest?

I am using the following JSON: http://www.kb.dk/tekst/mobil/aabningstider_en.json
When I try to parse it by the key "location" as such:
// get response in the form of a utf-8 encoded json string
NSString *jsonString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
// get most parent node from json string
NSDictionary *json = [jsonString JSONValue];
// get key-path from jason up to the point of json object
NSDictionary *locations = [json objectForKey:#"location"];
NSLog( #"%#", locations );
// iterate through all of the location objects in the json
for (NSDictionary *loc in locations )
{
// pull library name from the json object
NSString *name = [loc valueForKey:#"name"];
// add library data table arrays respectively
[ libraryNames addObject: ( ( name == nil | name.length > 0 ) ? name : #"UnNamed" ) ];
}
When I print the the object locations via NSLog:
{
address = "Universitetsparken 4, 3. etage, 2100 K\U00f8benhavn \U00d8";
desc = "";
lastUpdated = "";
latlng = "55.703124,12.559596";
link = "http://www.farma.ku.dk/index.php?id=3742";
name = "Faculty of Pharmaceutical Sciences Library";
parts = {
part = {
hour = {
day = "5.June Constitution Day (Denmark)";
open = Closed;
};
hours = {
hour = {
day = Friday;
open = "10-16";
};
};
name = main;
};
};
}
Which is only the last value for the "location" keys. Am I doing something wrong?
I tried validating the JSON via http://jsonlint.com/, however when I'd put in the JSON URL as above, it said "valid" - still only the last "locations" key was shown", however if I copy-paste it, it will not validate the JSON, and has to be fixed by removing new-lines from the string.
Also, when i try to parse the JSON and get the "name" fields, I get the following exception:
2012-05-08 15:37:04.941 iPhone App Tabbed[563:f803] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x68bfe70> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.'
*** First throw call stack:
(0x13dc052 0x156dd0a 0x13dbf11 0x9d2f0e 0x941841 0x940ca9 0x4593 0xf964e 0x114b89 0x1149bd 0x112f8a 0x112e2f 0x1148f4 0x13ddec9 0x365c2 0x3655a 0x25b569 0x13ddec9 0x365c2 0x3655a 0xdbb76 0xdc03f 0xdbbab 0x25dd1f 0x13ddec9 0x365c2 0x3655a 0xdbb76 0xdc03f 0xdb2fe 0x5ba30 0x5bc56 0x42384 0x35aa9 0x12c6fa9 0x13b01c5 0x1315022 0x131390a 0x1312db4 0x1312ccb 0x12c5879 0x12c593e 0x33a9b 0x281d 0x2785)
terminate called throwing an exception(lldb)
It would make more sense if the "locations" tag was an array object enclosed by square brackets ([]), however right now it's only an sequence of normal key-value pairs... Sadly, that's the JSON I have to work with.
Please help and thanks a great deal! :)
Sincerely,
Piotr.
The JSON you've got to work with may be valid, but it doesn't make much sense. It has one big dictionary with the location key repeated many times. Most JSON parser will simply return the last value for the repeated key. It would be best if you could change the structure to use an array instead, but if you cannot there's still hope. You can read the stream and stuff the values from the location keys into an array as they come out of it. This is how you'd do that:
#interface BadJsonHelper : NSObject
#property(strong) NSMutableArray *accumulator;
#end
#implementation BadJsonHelper
- (void)parser:(SBJsonStreamParser *)parser foundArray:(NSArray *)array {
// void
}
- (void)parser:(SBJsonStreamParser *)parser foundObject:(NSDictionary *)dict {
[accumulator addObject:dict];
}
#end
You can drop that little helper class at the top of your file, outside the #implementation section of the class where you're doing your work. (There's no need for the #interface and #implementation being in different files.)
In your code, you would use it like this:
BadJsonHelper *helper = [[BadJsonHelper alloc] init];
helper.accumulator = [NSMutableArray array];
SBJsonStreamParserAdapter *adapter = [[SBJsonStreamParserAdapter new] init];
adapter.delegate = helper;
adapter.levelsToSkip = 1;
SBJsonStreamParser *parser = [[SBJsonStreamParser alloc] init];
parser.delegate = adapter;
switch ([parser parse: responseData]) {
case SBJsonStreamParserComplete:
NSLog(#"%#", helper.accumulator);
break;
case SBJsonStreamParserWaitingForData:
NSLog(#"Didn't get all the JSON yet...");
break;
case SBJsonStreamParserError:
NSLog(#"Error: %#", parser.error);
break;
}
This example was originally adapted from the following test:
https://github.com/stig/json-framework/blob/master/Tests/StreamParserIntegrationTest.m
Update: I created a fully functional example project that loads the JSON asynchronously and parses it. This is available from github.
The JSON is valid, however there is a basic problem regarding the definition of the array of items.
Instead of defining an array of locations using brackets, the JSON redefines the same location key/value pair over and over again. In other words JSON initially says the value of location is the collection with name "The Black Diamond", but immediately after it redefines it with the collection with name "Faculty Library of Humanities" and so on till the last location Faculty of Pharmaceutical Sciences Library".
The same is true for parts and hours.
If you can't fix the result of the JSON and you really need to get it working you may want to modify the JSON removing the "location" keys and adding brackets properly.
Edit
Alternatively you may use an NSScanner and process the JSON result manually. Kinda hacky but it will work as long as the JSON format doesn't change significantly.
Edit
This snipped of code should do the work...
NSString *jsonString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
int indx = 1;
for (;;)
{
NSRange locationRange = [jsonString rangeOfString:#"\"location\":"];
if (locationRange.location == NSNotFound) break;
jsonString = [jsonString stringByReplacingCharactersInRange:locationRange
withString:[NSString stringWithFormat:#"\"location%d\":", indx++]];
}
NSDictionary *locations = [json objectForKey:#"location"];
As you can see, the result of JSON parsing by SBJson is a NSDictionary. A dictionary contains key/value pairs, and the keys are unique identifiers for the pairs.
The JSON data you need to handle is valid but not a good one. Per RFC 4627 - 2.2:
An object structure is represented as a pair of curly brackets surrounding zero or more name/value pairs (or members). A name is a string. A single colon comes after each name, separating the name from the value. A single comma separates a value from a following name. The names within an object SHOULD be unique.
Things like jQuery can parse the JSON also, but the result is the same as SBJson (the last one as the one). See Do JSON keys need to be unique?.
It is not a MUST, but it's still not a good practice. It would be much easier if you are able to change the structure of the JSON data on the server side (or even on the client side after receiving it) rather than parsing it as is.

xml parsing iphone, objective C?

i want to get data between xml tags? how to navigate? and get values..
im using wsdl2objc from google code:http://code.google.com/p/wsdl2objc/
output soapbody follows:
read instruction here: http://code.google.com/p/wsdl2objc/wiki/UsageInstructions
my header file: #import "MService.h"
how to get image source and text value????
please help me....
if([bodyPart isKindOfClass:[types_getFavoriteColorResponseType class]]) {
types_getFavoriteColorResponseType *body = (types_getFavoriteColorResponseType*)bodyPart;
// Now you can extract the color from the response
q.text = body.color;
continue;
}
Ок as far as I understand this is a part which extracts text data from your SOAP response.
BTW you need response to be processed via SAX or DOM? First example in given URL refers to DOM usage, whereas the second to SAX.
More than that I can not tell. Guess you have to read manual or find someone, who worked with this.
Use NSXMLParser, NSXMLParserDelegate for xml parsing, you can get the callbacks with proper values:
parser:didStartElement:namespaceURI:qualifiedName:attributes:
parser:foundCharacters:
parser:didEndElement:namespaceURI:qualifiedName:
Ref: http://developer.apple.com/library/ios/#documentation/cocoa/reference/NSXMLParserDelegate_Protocol/Reference/Reference.html
hey i got the result using sudzc.com
if ([result isKindOfClass:[MSalesPages class]]) {
NSLog(#"Response");
NSMutableArray* pageData = result.PageData;
for(MSalesPage* page in pageData){
NSLog(#"Inside for loop %#", page.Id);
NSMutableArray* images = page.Images;
NSMutableArray* textData = page.TextData;
for(MSalesImg* img in images){
NSLog(#"Image url %#",img.Src);
}
for(MSalesText* text in textData){
NSLog(#"Product Name %#",text.Value);
}
}
}
carefully check with the above xml, u will get the answer :)

Can't return a String value and append it...why?

I am writing an app and am having problems returning a simple string value, and I'm not sure why.
The function I am using (within a file called APIManager.m) is:
- (NSString*) returnVenueUrl {
NSString *venueUrl = [devEnvironment stringByAppendingString:#"venue/id/"];
return venueUrl;
}
I can return this properly by doing this in another .m file:
APIManager *apiManager = [APIManager apiManager];
NSLog(#"view venue URL is here: %#", [apiManager returnVenueUrl]);
But when I go to append a variable cast as a String onto it, I get nothing..
venueURL = [apiManager returnVenueUrl];
venueURL = [venueURL stringByAppendingString:venueId];
NSLog(#"the Full Venue URL is: %", venueURL);
If anyone has any advice on how to fix this, it would be much appreciated!
Use NSMutableString
NSString is immutable (aka non modifiable)

iPhone: variable type returned by yajl

I'm quite new to iphone programming and I want to do the following stuff:
get data from a JSON REST web server
parse the received data using YAJL
Draw a graph with those data using core-plot
So, 1th item is fine, I use ASIHttpRequest which runs as espected
3rd is almost fine (I still have to learn how to tune core-plot).
The problem I have is regarding 2nd item.
I use YAJL as it seems to be the faster parser, so why not give it a try :)
Here is the part of code that gets the data from the server and parse them:
// Get server data
response_data = [request responseData];
// Parse JSON received
self.arrayFromData = [response_data yajl_JSON];
NSLog(#"Array from data: %#", self.arrayFromData);
The parsing works quite well in fact, the NSLog output is something like:
2010-06-14 17:56:35.375 TEST_APP[3733:207] Array from data :
{
data = (
{
val = 1317;
date = "2010-06-10T15:50:01+02:00";
},
{
val = 1573;
date = "2010-06-10T16:20:01+02:00";
},
........
{
val = 840;
date = "2010-06-11T14:50:01+02:00";
},
{
val = 1265;
date = "2010-06-11T15:20:01+02:00";
}
);
from = "2010-06-10T15:50:01+02:00";
to = "2010-06-11T15:20:01+02:00";
max = "2590";
}
According to th yajl-objc explanations http://github.com/gabriel/yajl-objc, the parsing returns a NSArray...
The thing is... I do not know how to get all the values from it as for me it looks more like a NSDictionary than a NSArray...
Could you please help ?
Thanks a lot,
Luc
edit1: it happens that this object is actually a NSCFDictionary (!), I am still not able to get value from it, when I try the objectFromKey method (that should work on a Dictionary, no ?) it fails.
It's returning an NSDictionary. NSCFDictionary is a private subclass and is immaterial to this discussion. So it looks like you'd retrieve stuff like:
NSDictionary * responseDictionary = ...;
NSArray * dataArray = [responseDictionary objectForKey:#"data"];
for (NSDictionary * dataPair in dataArray) {
NSLog(#"val: %#, date: %#", [dataPair objectForKey:#"val"], [dataPair objectForKey:#"date"]);
}