XML: going up to ancestor with certain attribute (while being in a for loop) Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Kranich_Ibykus
Posts: 36
Joined: 19 Nov 2016, 05:19

XML: going up to ancestor with certain attribute (while being in a for loop)

23 Nov 2016, 04:13

Hi guys!

Thanks to a previous answer in this wonderful forum, I managed to begin my little project where I want to iterate over an XML file.

My question this time regards how to get the ancestor node of one node I am already in through my for loop.

Can I somehow jump from the variable „Einzelkommentar“ to an ancestor node? What I need to do is find the nearest ancestor node of „Einzelkommentar“ (or simply the node „Comment“) which contains an attribute called „id“.

Also, could somebody please explain to me why the last iteration in the code works, regarding “nurname”? What it does is taking the entire file name and thus only giving out the file name without any of the previous items. It works but I really don’t understand why: if I change “Filename0” to “Filename1” it doesn’t work for example anymore. Also, why do I have to write “Filename%nurname%”, what das that do?

Please find my code below.

Code: Select all

FileEncoding, UTF-8
Filelocation := "C:\Users\rschuber\Desktop\XML in AHK\Hamlet.sdlxliff"
StringSplit, Filename, Filelocation, \
FileRead, xmldata, %Filelocation%
xmldata := StrReplace(xmldata,"xmlns=","QWERTYrandomweathering=")
doc := ComObjCreate("MSXML2.DOMDocument.6.0")
doc.async := false
doc.loadXML(xmldata)

AlleKommentare := doc.selectNodes("//Comment")
 oExcel := ComObjCreate("Excel.Application")
 oExcel.Workbooks.Add

increase := 1
For Einzelkommentar in AlleKommentare
	{
		jeder := Einzelkommentar["text"]
		oExcel.Cells(increase,1).Value := jeder
		
		ida := Einzelkommentar.getAttribute("date")
		StringTrimRight, ida, ida, 14
		ida := StrReplace(ida, "T", "`n")							; die Uhrzeit wird mit Linebreak geschrieben
		oExcel.Cells(increase,2).Value := ida

		usera := Einzelkommentar.getAttribute("user")
		oExcel.Cells(increase,3).Value := usera

		nurname := Filename0
		oExcel.Cells(increase,4).Value := Filename%nurname%

		increase += 1
	}

oExcel.Worksheets(1).ListObjects.Add
oExcel.Cells(1,1).Value := "Comment"
oExcel.Cells(1,2).Value := "Date"
oExcel.Cells(1,3).Value := "User"
oExcel.Cells(1,4).Value := "Filename"
oExcel.Visible := 1		

ExitApp
And here a quick view of my XML. I maanged to jump to each comment with "//Comment". What I now need to do is going up to "cmt-def" which has an id and get that id. I want to do that with "ancestor" though because there will be some instances later on (see XML example 2 below) where I need to retrieve the id and then iterate ancestor-wise as often up above until I hit a segment with the attribute "mid".

Code: Select all

	<cmt-defs>
			<cmt-def id="c894f2ce-599c-4b07-a685-734a67e25ac4">
				<Comments>
					<Comment severity="Low" user="PC" date="2016-11-07T21:54:25.1610721+00:00" version="1.0">Comment 1 on "Frage"</Comment>
				</Comments>

Code: Select all

				
	<mrk mtype="seg" mid="1">Sein oder Nichtsein; das ist hier die
		<mrk mtype="x-sdl-comment" sdl:cid="c894f2ce-599c-4b07-a685-734a67e25ac4">
			<mrk mtype="x-sdl-deleted" sdl:revid="ccc64fec-0fcb-447d-8803-ff4f2f26ccc9">
				<mrk mtype="x-sdl-comment" sdl:cid="c894f2ce-599c-4b07-a685-734a67e25ac4">Frage</mrk>
			</mrk>
		<mrk mtype="x-sdl-added" sdl:revid="8753f05b-628b-4c1b-b33b-f9ea9961e06c">I just deleted "Frage", but the comment flag is still in</mrk>
	</mrk>:
Fee
Posts: 57
Joined: 05 Aug 2016, 14:30

Re: XML: going up to ancestor with certain attribute (while being in a for loop)

23 Nov 2016, 04:57

Kranich_Ibykus wrote:Can I somehow jump from the variable „Einzelkommentar“ to an ancestor node? What I need to do is find the nearest ancestor node of „Einzelkommentar“ (or simply the node „Comment“) which contains an attribute called „id“.
Crude way of doing it:

Code: Select all

For Einzelkommentar in AlleKommentare {
    _this:=Einzelkommentar
    while !_this.getAttribute("id")
        _this:=_this.ParentNode
    MsgBox % "Found <" _this.nodeName "> with ID:`n" _this.getAttribute("id")
}
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: XML: going up to ancestor with certain attribute (while being in a for loop)

23 Nov 2016, 05:04

For your first question, see parentNode

Filename0 contains the number of elements/variables which was stored/created by StringSplit, so nurname := Filename0 will store the number of the last element/variable in nurname, and Filename%nurname% will resolve to this element/variable (see StringSplit).
Kranich_Ibykus
Posts: 36
Joined: 19 Nov 2016, 05:19

Re: XML: going up to ancestor with certain attribute (while being in a for loop)

23 Nov 2016, 10:21

Fee wrote: Crude way of doing it:
Hi Fee, thank you very much for this! I will test it out once I'm home.

Could you please also give me an explanation of your code so that I really grasp the concept? That would be terrific! :)
just me wrote:For your first question, see parentNode

Filename0 contains the number of elements/variables which was stored/created by StringSplit, so nurname := Filename0 will store the number of the last element/variable in nurname, and Filename%nurname% will resolve to this element/variable (see StringSplit).
Thank you for your explanation, really helpful! I didn't kno that "array0" stores the number of arrays ,good to know! :) I will have a look at parentNode. (Fee uses it as well.)
Fee
Posts: 57
Joined: 05 Aug 2016, 14:30

Re: XML: going up to ancestor with certain attribute (while being in a for loop)  Topic is solved

23 Nov 2016, 10:58

Code: Select all

For Einzelkommentar in AlleKommentare {
    _this:=Einzelkommentar
    while !_this.getAttribute("id")
        _this:=_this.ParentNode
    MsgBox % "Found <" _this.nodeName "> with ID:`n" _this.getAttribute("id")
}

http://www.w3schools.com/xml/prop_node_parentnode.asp

Code: Select all

<parent>
    <element>
</parent>
.parentNode returns... parent node of element. That should be self explanatory.
https://autohotkey.com/docs/commands/While.htm
while is a kind of loop that "Performs a series of commands repeatedly until the specified expression evaluates to false".
In the example you can look at the expression as if it is:
check if following is FALSE (!): _this element does have value assigned to attribute called ID
Which means that the loop continues as long as _this doesn't have attribute ID.
What we do in loop is just change _this to it's parent node. Which means with each iteration of loop we are going up until we find node with attribute/value ID which is now stored in _this variable.
Alternatively there is xpath solution, which does pretty much the same thing:
http://www.w3schools.com/xml/xpath_intro.asp

Code: Select all

For Einzelkommentar in AlleKommentare {
    foo:=Einzelkommentar.selectSingleNode("ancestor::*[@id]")
    MsgBox % "Found <" foo.nodeName "> with ID:`n" foo.getAttribute("id")
}
Kranich_Ibykus
Posts: 36
Joined: 19 Nov 2016, 05:19

Re: XML: going up to ancestor with certain attribute (while being in a for loop)

23 Nov 2016, 11:24

Oh man, Fee, I hope you know how awesome you are! You are fantastic! Thank you SO much :)
Kranich_Ibykus
Posts: 36
Joined: 19 Nov 2016, 05:19

Re: XML: going up to ancestor with certain attribute (while being in a for loop)

24 Nov 2016, 04:32

@Fee: Can I please ask you a question again?

I now continued a little on the code and I store the ID i get thanks to you in the variable "ids". Then, I want to step further down in the xml file to find any node which has an attribute called "sdl:cid" and which contains the id we stored before. Somehow it doesn't work though. But when I enter the id manually ( target1 := foo.selectSingleNode("following::*[@*='c894f2ce-599c-4b07-a685-734a67e25ac4']" ), I do get the node! I assume that my way of usigin variables in xpath is not correct.

Please find my code below. (Note: Since I couldn't find any solution on how to escape the colon character in xpath, I just decided to use the asterisk (*) wildcard instead. Do you also know how to escape colons in xpath in AutoHotkey?)

Code: Select all

; XML-ID OF THE COMMENT, VIA XPATH (NEEDED TO JUMP LATER ON TO THE SEGMENT)
foo := Einzelkommentar.selectSingleNode("ancestor::*[@id]")
ids := foo.getAttribute("id")
oExcel.Cells(increase,5).Value := ids
	
; THE NODE WHICH ATTRIBUTE IS THE SAME AS THE ID ABOVE
target1 := foo.selectSingleNode("following::*[@*=ids]")
MsgBox, % target1.nodeName
Fee
Posts: 57
Joined: 05 Aug 2016, 14:30

Re: XML: going up to ancestor with certain attribute (while being in a for loop)

24 Nov 2016, 07:57

Code: Select all

; target1 := foo.selectSingleNode("following::*[@*=ids]") ; Won't work
target1 := foo.selectSingleNode("following::*[@*=""" ids """]") ; Will work

Kranich_Ibykus wrote:how to escape the colon character in xpath
That's not really colon per se, it's namespace, I don't have head for it right now.
MSDN: XML Namespaces and How They Affect XPath and XSLT
MSDN: SelectionNamespaces Property
Kranich_Ibykus
Posts: 36
Joined: 19 Nov 2016, 05:19

Re: XML: going up to ancestor with certain attribute (while being in a for loop)

25 Nov 2016, 05:44

Hi Fee, Thank you as always for your help, I now managed to push through with that problem :)

Now I'm onto the last bit. Let's see if I can figure it out alone, otherwise I will maybe have to ask again.
Kranich_Ibykus
Posts: 36
Joined: 19 Nov 2016, 05:19

Re: XML: going up to ancestor with certain attribute (while being in a for loop)

25 Nov 2016, 10:47

My last question: but the hardest bit is now to get all the text from a node except for text which is in between nodes which have the attribute “mtype=’x-sdl-added’”. This is what the xml looks like:

Code: Select all

<g id="2">
      <mrk mtype="seg" mid="1">Sein oder Nichtsein; das ist hier die
            <mrk mtype="x-sdl-comment" sdl:cid="c894f2ce-599c-4b07-a685-734a67e25ac4">
                  <mrk mtype="x-sdl-deleted" sdl:revid="ccc64fec-0fcb-447d-8803-ff4f2f26ccc9">
                        <mrk mtype="x-sdl-comment" sdl:cid="c894f2ce-599c-4b07-a685-734a67e25ac4">Frage</mrk>
                  </mrk>
                  <mrk mtype="x-sdl-added" sdl:revid="8753f05b-628b-4c1b-b33b-f9ea9961e06c">I just deleted "Frage", but the comment flag is still in</mrk>
            </mrk>:
      </mrk>
So, what I want to have in the end are the following two:

Code: Select all

Sein oder NIchtsein; das ist hier die Frage

Code: Select all

Sein oder NIchtsein; das ist hier dieI just deleted "Frage", but the comment flag is still in</mrk>
Everything I can do so far is showing all text though.

I already tried a lot but I just don’t get xpath to select all nodes except for the ones specified. I was using this one so far:
trgc := doc.selectSingleNode("//target/g[@id= """ gidb """][not(@mtype='x-sdl-added')]")
MsgBox, % trgc["text"]
When I use the following one, text gets excluded, but EVERYTHING, so that I don’t get any text at all in the end.
trgc := doc.selectSingleNode("//target/g[@id= """ gidb """][not(contains('x-sdl-added', @mtype))]")
MsgBox, % trgc["text"]
I was also trying if I could do it with a “while allmarks.hasChildNodes)” loop, but also this is not cool, because some nodes have several children. Do you have any idea?

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: jaka1, OrangeCat and 158 guests