I have question about a basic xml file I'm parsing and just putting in simple nextlines(Enters).
I'll try to explain my problem with this next example.
I'm( still) building an xml tree and all it has to do ( this is a testtree ) is put the summary in an itemlist. I then export it to a plist so I can see if everything is done correctly.
A method that does this is in the parser which looks like this
if([elementName isEqualToString:#"Book"]) {
[appDelegate.books addObject:aBook];
[aBook release];
aBook = nil;
}
else
{
[aBook setValue:currentElementValue forKey:elementName];
NSString *directions = [NSString stringWithFormat:currentElementValue];
[directionTree = setObject:directions forKey:#"directions"];
}
[currentElementValue release];
currentElementValue = nil;
}
the export for the plistfile happens at the endtag of books.
Below is the first xmlfile
<?xml version="1.0" encoding="UTF-8"?>
<Books><Book id="1"><summary>Ero adn the ancient quest to measure the globe.</summary></Book><Book id="2"><summary>how the scientific revolution began.</summary></Book></Books>
This is my output
http://img139.imageshack.us/img139/9175/picture6rtn.png
If I make some adjustments like here
<?xml version="1.0" encoding="UTF-8"?>
<Books><Book id="1">
<summary>Ero adn the ancient quest to measure the globe.</summary>
</Book>
<Book id="2">
<summary>how the scientific revolution began.</summary>
</Book>
</Books>
My directions key with type string remains empty...
http://img248.imageshack.us/img248/5838/picture7y.png
I never knew that if I just put in an enter it would have such an influence.
Does anyone know a solution to this since my real xml file looks like this.
ps. the funny thing is I can actually see ( when debugging)my directions string (NSString directions ) fill up with the currentElementValue in both cases.
Instrument your code; specifically, just above the line that reads...
[directionTree setObject:directions forKey:#"directions"];
... (I removed a stray =) try adding ...
NSLog(#"setting directions to '%#'", directions);
I bet you'll see the above is logged multiple times per element. Specifically, the newline between the </summary> and the </book> tag is, in and of itself, an element just like the text in the <summary></summary> tag is an element.
Now, you could continue down the path of trying to special case for this that and the other, but that would be wrong.
You need to parse the XML as a structured document -- as a tree of nodes. Specifically, you should be looking for the <summary> tag somewhere and then grabbing the element that hangs below it (that should be of, IIRC, the TEXT type in XML parlance -- been a while).
Or, better yet, use one of the XML parsing APIs on the system. NSXMLDocument comes immediately to mind. If working on the iPhone (which this question didn't indicate), you'll need to use NSXMLParser and not NSXMLDocument as it is not available.
Or, even better, since this looks like pretty straightforward XML encapsulation of a regular data schema, use CoreData. CoreData is ideal for storing this kind of information. If your XML is intended to be an interchange format, you won't want to use CoreData as the XML it produces is entirely of its own design.
Related
I have a plist and i want to convert it to xml. The xml itself is going to be around 1.2mb in size. What the best way to generate this xml? Simply with a NSMutableString? I am just worried about the performance issues and wether there is a better way to generate xml.
Thanks
For those wondering, what I have right now is something like this:
NSString *xml = [NSString stringWithFormat:#"<Sheet>%#</Sheet>", [self getSheetXMLString]];
and then, in getSheetXMLString method, i have more methods like above which drill down deep until the plist is fully transversed.
Thanks again.
What do you plan to do with the XML, if it is to output over a network or write to a file then instead of creating a NSString you could just write straight out to the network/file. If you plan to do manipulation if the XML you may want to consider libxml2, which is a C library included in iOS.
I've been using NSXMLParser for parsing xml and was successful in launching apps ... Recently I'm facing problems with xml ... Since xml is heavy weight. It increases the memory over head ... I'm planning to shift to plist(since it reduces the memory overhead considerably). And have no idea where to begin with ... I've searched all through the net and was not successful could u guys gimme me a sample code or even a working url containing a plist ... Is more than enough ... I'd be so grateful if u guys could help me out.
if you have control over the format of the data, have you considered using JSON instead?
You can use the CWXMLTranslator from https://github.com/jayway/CWFoundation for easy translation from XML to domain object.
Assume you have this domain class:
#interface Book : NSObject {
}
#property(copy) NSString* author;
#property(copy) NSString* title;
#end
And this XML:
<books>
<book>
<author>James Joyce</author>
<title>Ulysses</title>
</book>
<!-- MORE BOOKS HERE -->
</book>
You only need to define a translation file, name it Book.xmltranslation, and add this:
book +> #root : Book {
author >> author;
title >> title;
}
That would then be used to fetch and trabnslate the XML fromt he server into live instances of your Book class like this:
NSArray* books = [CWXMLTranslator translateContentsOfURL:url
withTranslationNamed:#"Book"
delegate:nil
error:NULL];
This is the easiest usecase available, the translation DSL can even be written inline if you want. The CWXMLTranslator support much more complex operations also, like type convertions to dates, URLs, numbers, etc. As well as nested types, and direct translation to Core Data managed objects.
The CWFoundation project contains all documentation you need, and a sample project that parses RSS feeds.
Modern plists are XML: Property Lists. It's possible to use and create binary plists, but they're just a form of binary XML. That can in turn mean some reduction in overhead, but at the cost of readability. JSON can be smaller than equivalent XML, though not always.
Leaving all that aside, plists tend to be more verbose than equivalent XML. Compare the XML:
<book>
<author>James Joyce</author>
<title>Ulysses</title>
</book>
with an equivalent plist:
<dict>
<key>author</key>
<string>James Joyce</string>
<key>title</key>
<string>Ulysses</string>
</dict>
I am working on an application where I need to parse some XML files that consists CDATA tags. Parsing ordinary xml is quite straight forward but I am facing problems to retrieve data that is inside the CDATA tag.
The parser:foundCDATA: method is being called for each CDATA tag encountered where the parameter CDATABlock is of NSData type.
Please suggest a way to parse the CDATA tag.
If you need to extract the string from CDATA, you could use this block in foundCDATA:
NSMutableString *lStr = [[NSMutableString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
i have taken string from the CDATA tag as Oleg Danu said but still it consists XML tags.So i wrote that entire string to a file,whenever CDATA found, and created one more XMLParser by setting the delegate to same class as that of original Parser.All tags in CDATA tag are parsed properly by the secondary parser.The parser will be released at the end of the foundCDATA method and actual parsing continues as it is.
I have a sample set of XML returned back:
<rsp stat="ok">
<site>
<id>1234</id>
<name>testAddress</name>
<hostname>anotherName</hostname>
...
</site>
<site>
<id>56789</id>
<name>ba</name>
<hostname>alphatest</hostname>
...
</site>
</rsp>
I want to extract everything within <name></name> but not the tags themselves, and to have that only for the first instance (or based on some other test select which item).
Is this possible with regex?
<disclaimer>I don't use Objective-C</disclaimer>
You should be using an XML parser, not regexes. XML is not a regular language, hence not easely parseable by a regular expression. Don't do it.
Never use regular expressions or basic string parsing to process XML. Every language in common usage right now has perfectly good XML support. XML is a deceptively complex standard and it's unlikely your code will be correct in the sense that it will properly parse all well-formed XML input, and even it if does, you're wasting your time because (as just mentioned) every language in common usage has XML support. It is unprofessional to use regular expressions to parse XML.
You could use Expat, with has Objective C bindings.
Apple's options are:
The CF xml parser
The tree based Cocoa parser (10.4 only)
Without knowing your language or environment, here are some perl expressions. Hopefully it will give you the right idea for your application.
Your regular expression to capture the text content of a tag would look something like this:
m/>([^<]*)</
This will capture the content in each tag. You will have to loop on the match to extract all content. Note that this does not account for self-terminated tags. You would need a regex engine with negative lookbehinds to accomplish that. Without knowing your environment, it's hard to say if it would be supported.
You could also just strip all tags from your source using something like:
s/<[^>]*>//g
Also depending on your environment, if you can use an XML-parsing library, it will make your life much easier. After all, by taking the regex approach, you lose everything that XML really offers you (structured data, context awareness, etc).
The best tool for this kind of task is XPath.
NSURL *rspURL = [NSURL fileURLWithPath:[#"~/rsp.xml" stringByExpandingTildeInPath]];
NSXMLDocument *document = [[[NSXMLDocument alloc] initWithContentsOfURL:rspURL options:NSXMLNodeOptionsNone error:NULL] autorelease];
NSArray *nodes = [document nodesForXPath:#"/rsp/site[1]/name" error:NULL];
NSString *name = [nodes count] > 0 ? [[nodes objectAtIndex:0] stringValue] : nil;
If you want the name of the site which has id 56789, use this XPath: /rsp/site[id='56789']/name instead. I suggest you read W3Schools XPath tutorial for a quick overview of the XPath syntax.
As others say, you should really be using NSXMLParser for this sort of thing.
HOWEVER, if you only need to extract the stuff in the name tags, then RegexKitLite can do it quite easily:
NSString * xmlString = ...;
NSArray * captures = [xmlString arrayOfCaptureComponentsMatchedByRegex:#"<name>(.*?)</name>"];
for (NSArray * captureGroup in captures) {
NSLog(#"Name: %#", [captureGroup objectAtIndex:1];
}
Careful about namespaces:
<prefix:name xmlns:prefix="">testAddress</prefix:name>
is equivalent XML that will break regexp based code. For XML, use an XML parser. XPath is your friend for things like this. The XPath code below will return a sequence of strings with the info you want:
./rsp/site/name/text()
Cocoa has NSXML support for XPath.
I am using TouchXml because of the given limitations of NSXML on the actual iPhone. Anyway, I'm just starting out with Objective-C, I come from a C# background, and felt like learning something new..anyhow.. here is my xml file...
<?xml version="1.0" encoding="utf-8"?>
<FundInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/webservices">
<FundsReleaseDate>2009-02-11T00:00:00</FundsReleaseDate>
<FundValue>7800</FundValue>
<FundShares>1000</FundShares>
</FundInfo>
I'm trying to get 7800, i.e FundValue. Can someone point me in the correct direction, I am using the TouchXml CXMLDocument and myParser is of type CXMLDocument, I have tried
NSArray *nodes = [myParser nodesForXPath:#"//FundInfo" error:&err];
Basically nodes evaluates to nil
if ([nodes count] > 0 ) {
amount = [nodes objectAtIndex:1];
}
UPDATE 1: I have abandoned parsing using XPath, and have replaced it with NSURLRequest, so now I have the entire XML in a string, but my rude introduction to objective-C continues...I just realized how spoiled I have become to the .NET BCL where things like Regex are so easily available.
UPDATE2: I've figured out how to use RegexKitLite for regex.
So the question now is, how do I get the "7800" from inside FundValue which is now one big string. I have confirmed I have it in my NSString by writing it using NSLog.
The problem is that your XML has a namespace, which means your XPath does not actually match. Unfortunately there is no default mapping, and XPath does not actually define a good way to handle this internally, so you can't fix it simply by changing the XPath. Instead you need to inform the interpreter how you want to map it in your XPath.
It looks like TouchXML implements support for this via:
- (NSArray *)nodesForXPath:(NSString *)xpath namespaceMappings:(NSDictionary *)inNamespaceMappings error:(NSError **)error;
So you can try something like:
NSDictionary *mappings = [NSDictionary dictionaryWithObject:#"http://tempuri.org/webservices" forKey:#"tempuri"];
[myParser nodesForXPath:#"//tempuri:FundInfo" namespaceMappings:mappings error:&err];
I had a similar problem. ANY selection on an XML that has a namespace specified (xmlns="http://somenamespace") will result in no nodes found. Very strange considering it does support the additional namespace formats such as xmlns:somenamespace="http://somenamespace".
In any case, by far the easiest thing to do is do a string replace and replace xmlns="http://tempuri.org/webservices with an empty string.
Overall I like touchxml, but I can't believe this bug still exists.
Try
NSArray *nodes = [myParser nodesForXPath:#"/FundInfo/*" error:&err];