How do I loop nodes using NSXML on the Mac and change each node's text value - objective-c

have been stuck on this for days now- How can I loop every node in an XML document and change the text value of the node.
For example go from this:
<root>
<node1>some text</node1>
<node2>
<node3>some more text</node3>
</node2>
</root>
to something like:
<root>
<node1>updated text</node1>
<node2>
<node3>updated text</node3>
</node2>
</root>
The code I have that doesn't work is:
NSArray *nodes = [xmlDoc nodesForXPath:#"//*"];
for (NSXMLElement *node in nodes) {
//In time a function call will go here to change the text:
NSString *newVal = #"updated text";
[node setStringValue:newVal];
}
Although it seems to loop ok when i check the contents of XMLDoc it then has this in it:
<root>
updated text
</root>
Please help if you can I have tried repeated google searches and am pulling my (already thinning) hair out - surely this should be fairly simple?!
Matt.

It's your xpath expression. //* selects all nodes in the document including the root node. So at some point in the iteration you are setting the string value of the root node which is apparently wiping out all its previous child nodes.
I'm not an expert in xpath, but something like:
/root//*
might do the trick except that node3 will get wiped out for the same reasons. If you look through The XPath tutorial there should be a way in there of selecting all text nodes.

Related

Check if XML nodes are empty in SQL

Hi I am new to XML manipulation, my question would be if there is a possibility of detecting if the XML node is an empty node like this: <gen:nodeName />
I am able to manipulate single nodes however I would be interested if there is an approach like a loop or recursive function that could save some time doing manual labor looking trough every single node. I have no idea how to approach this problem though.
Thanks for help.
You did not specify the dialect of SQL ([sql] is not enough, please specify always the RDBMS incl. version).
This is for SQL-Server, but the semantics should be the same.
DECLARE #xml XML=
N'<root>
<SelfClosing />
<NoContent></NoContent>
<BlankContent> </BlankContent>
<HasContent>blah</HasContent>
<HasContent>other</HasContent>
</root>';
SELECT #xml.query(N'/root/*') AS AnyBelowRoor --All elements
,#xml.query(N'/root/*[text()]') AS AnyWithTextNode --blah and other
,#xml.query(N'/root/*[not(text())]') AS NoText --no text
,#xml.query(N'/root/*[text()="blah"]') AS AnyWithTextNode--blah only
The <SelfClosing /> is semantically the same as the <NoContent><NoContent>. There is no difference.
It might be a surprise, but a blank as content is taken as empty too.
So the check for empty or not empty is the check for the existance of a text() node. one can negate this with not() to find all without a text().
Interesting: The result for NoText comes back as this (SQL-Server)
<SelfClosing />
<NoContent />
<BlankContent />
The three elements are implicitly returned in the shortest format.

Get value through one of the xmlnode property for currentnode

My current xml node is :
<Item xsi:type="itm:Resource">
<ID>10</ID>
</Item>
I want to read the whole tag and search whether Resource is there or not in that tag :
SelectSingleNode.OuterXml.Contains("Resource")
But Outer xml is considering all the tags inside Item , I just want for the current node
Have tried other properties like name,value which indeeds return only "Item"
I have done it in a slightly different way
SelectSingleNode("xPath").Attributes(0).Value.Contains("Resource")
Hope this helps

Edit XML tag by attribute

I have a XML document which looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Configuration>
<Data key="dailyKey">19283</Data>
</Configuration>
And in my vb.net program I want to change the value from "<Data>" by the attribute "dailyKey"
I have tried to understand myself on this but cannot figure out how to edit TAG by ATTRIBUTE
Please help, Richard
You could use XPath expressions and the SelectSingleNode method like this:
Dim node = xmlDoc.SelectSingleNode("//data[#key=""dailyKey""]")
Then you can modify the value of node as you wish to. You can find more XPath examples at MSDN.
The workflow in its entirety would be:
1. Load the XML Document for manipulation
You can use the XmlDocument class to load (and subsequently save) your XML Document like this:
Dim xmlDoc As New XmlDocument()
xmlDoc.Load("<Here goes your url // You can also feed in a stream to this method>")
2. Locate the node you want to modify
As mentioned earlier, use the SelectSingleNode function to locate the node you are trying to modify the value of. It takes an XPath expression.
Dim node = xmlDoc.SelectSingleNode("//data[#key=""dailyKey""]")
3. Modify the node
You can now edit the node (tag) in whatever way you wish. It seems you want to edit the contained value. Do it by changing the Value property of the XmlNode:
node.Value = 224062 'Random value. Change to suit your needs.
4. Save the XML Document (Obviously :P)
xmlDoc.Save()

Hpple - Reading a tag with an attribute

I'm trying to parse a XML,
<entry>
<title type="html"><![CDATA[TITLE]]></title>
</entry>
Using Hpple, I'm trying to read the
NSArray *array = [xpathParser searchWithXPathQuery:#"//entry/title[#type='html']"];
But the returned value is null. What I'm doing wrong?
As hpple is a wrapper over XPathQuery, you might want to check it this answer
tl;dr Try adding /text() to your XPath query.
Also it looks like Hpple's author uses a different function call to search by XPath in his Unit Test for hpple; for example NSArray * a = [doc search:#"//a[#class='sponsor']"];, perhaps its worth to give that a shot.

xml parsing in iPhone and getting other tags with same names

Let me try to explain as clear as possible what I mean exactly with this question.
The xml looks instead like this
<Books>
<Book id="1">
<title>Circumference</title>
<author>Nicholas Nicastro</author>
<summary>Eratosthenes and the Ancient Quest to Measure the Globe.</summary>
</Book>
<Book id="2">
<title>Copernicus Secret</title>
<author>Jack Repcheck</author>
<summary>How the scientific revolution began</summary>
</Book>
</Books>
It will look like this
<Books>
<Book id="1">
<title>Circumference</title>
<author>Nicholas Nicastro</author>
<summary id ='1'>Eratosthenes and the Ancient Quest to Measure the Globe.</summary>
<summary id ='2'>Eratosthenes more info in another tag.</summary>
<summary id ='3'>Eratosthenes and again another tag.</summary>
<summary id ='4'>Eratosthenes and the final tag another one here</summary>
</Book>
<Book id="2">
<title>Copernicus Secret</title>
<author>Jack Repcheck</author>
<summary id ='1'>How the scientific revolution began</summary>
<summary id ='2'>Eratosthenes more info in another tag.</summary>
<summary id ='3'>Eratosthenes and again another tag.</summary>
<summary id ='4'>Eratosthenes and the final tag another one here</summary>
</Book>
</Books>
Now if I follow the instruction on the site listed above , it doesn't explain how to handle summary 2,3,4( the xml i need to parse looks like that) and how I can show their output. All I will get is the last line. Does anyone have an idea about how I can get the other ones as well( meaning 2,3 in this case it seems to show only the last one since that's probably the last in the currentElementValue ).
I'm a bit confused would I have to address the attribute here as well or should I create a new search tag in my parser?
I think this is what you need to be looking at, you could grab the value of the id field from the attributes and using that value assign it to a variable which you can then use.
So I might have something this in my didStartElement (where attributes is a variable declared in the header):
if([elementName isEqualToString:#"Summary"]){
attributes = attributeDict;
}
Then something like this in my foundCharacters:
if([[attributes valueForKey:#"id"] intValue] == 1){
doSomething
}else if([[attributes valueForKey:#"id"] intValue] == 2){
doSomethingElse
}...
and so on until you've got all your data out.
N.B. This is 100% untested code but I'm confident it might work...
You will need to keep track of the summary elements you have parsed so far by keeping all the values in some container like an array: NSMutableArray. Thus, instead of an NSString to keep the summary, you'd have an NSMutableArray to hold the list of summaries you have parsed.
Whenever you encounter a summary in your parser, you don't set the NSString summary to the string you just read (which replaces the old value and explains why you only get the last summary tag). Instead, you store it as a new string and add that string to your NSMutableArray.
The problem with the design in the blog post you linked to is that it uses keyValueCoding to set the properties in the Book object and that doesn't facilitate adding items to an array item very well. Hence, you will need to include some special handling for the summary element in the parser and add methods to the Book class that allow you to add items to the summary array. See this post on how to also do that with KVC.