Hello all !
Even though it seems there is no much hype anymore around XML I will still share a few things in case some would to handle some of their configuration in XML or for other purposes.
I am using a lot XML libraries by Coco visible on the old forum, and I thought first sharing the info could be useful:
https://autohotkey.com/board/topic/8919 ... parse-xml/
and XConfig
https://github.com/c...oHotkey-XConfig)
and here are a few functions I have coded to help me do some specific useful tasks.
I hope someone can be interested
XMLFCT_GetXPathToNode(Node) : get the Xpath from a node
(No dependancy)
Adapted from
https://stackoverflow.com/questions/241 ... e-instance
PS : not sure about the conversion of GetXPathToNode(((XmlAttribute)node).OwnerElement) "/@" node.Name
Code: Select all
;XMLFCT_GetXPathToNode
;**********************************
;Returns the Xpath from an XML node
XMLFCT_GetXPathToNode(XMLnode)
{
; if (node.NodeType == XmlNodeType.Attribute)
; tooltip % "NodeTypeString " node.NodeTypeString " NodeName " node.NodeName
if (XMLnode.NodeTypeString = "attribute")
{
; // attributes have an OwnerElement, not a ParentNode; also they have
; // to be matched by name, not found by position
; return GetXPathToNode(((XmlAttribute)node).OwnerElement) "/@" node.Name
return XMLFCT_GetXPathToNode(XMLnode.OwnerElement) "/@" XMLnode.NodeName
}
; if IsObject(node.ParentNode)
if (XMLnode.ParentNode="")
{
; // the only node with no parent is the root node, which has no path
return
}
; // Get the Index
indexInParent = 1
siblingNode := XMLnode.PreviousSibling
; // Loop thru all Siblings
; while IsObject(siblingNode)
while (siblingNode!="")
{
; // Increase the Index if the Sibling has the same Name
if (siblingNode.NodeName = XMLnode.NodeName)
{
; msgbox % "Sibling has same name " node.NodeName
indexInParent++
}
siblingNode := siblingNode.PreviousSibling
}
; // the path to a node is the path to its parent, plus "/node()[n]", where n is its position among its siblings.
return XMLFCT_GetXPathToNode(XMLnode.ParentNode) "/" XMLnode.NodeName "[" indexInParent "]"
}
And to get the Xpath without the last position from a XPath of a node which was including its position.
An example of their use will be demonstrated in the XMLFCT_CompareXMLbyElementNamesAndPosition function toward the end of this post.
Code: Select all
;XMLFCT_GetParentPathfromXpath
;Returns the Parent XPath from a Node XPath
XMLFCT_GetParentPathfromXpath(Lcl_Xpath) {
FoundPos := InStr(Lcl_Xpath, "/", false, 0, 1)
NewStr := SubStr(Lcl_Xpath, 1, FoundPos - 1)
return NewStr
}
;XMLFCT_DeletePosfromXpath
;Returns a Node XPath without its position (stopping at its name)
XMLFCT_DeletePosfromXpath(Lcl_Xpath) {
FoundPos := InStr(Lcl_Xpath, "[", false, 0, 1)
NewStr := SubStr(Lcl_Xpath, 1, FoundPos - 1)
return NewStr
}
PS : For the following functions, please understand that these functions have to be tweaked the way you want to compare the XML files.
There is no chance of having a one-suits-all solution.
So : The following functions were intending to compare XML element names from 2 files which have unique combinations of Parent/Child names. If an element is not recognized by its parent and own name then the whole element (and its children/text) will be copied from the default to the compared file. If the element was present (same parent and same name) then nothing more will be compared/added : meaning the text/attributes of these elements can be different and will stay the same.
The use case here was to compare two XML language files : the default one and a custom one. If the custom one had a missing node, then the one from default file would be copied so that even though the translation would not be there, there would still be the text in default language to avoid errors and missing texts.
Other cases:
But if you have some elements with same names in the same files, you will probably have to use a different mechanism that will grab the position of elements in addition to the names. But in this case the elements will have to be organized in the same order in the default and compared files. You can see my attempt in my next post : XMLFCT_CompareXMLbyElementNamesAndPosition().
If you want to compare attributes/text you will have to add these comparisons to the functions.
XMLFCT_XMLFCT_PuttingDefaultXML(DefaultXmlStrngOrObject,ByRef ExistingXMLObject, ExistingXMLPathToSave)
Compare a Default XML String or Object to an Existing XML Object and add absent nodes to the ExistingXML.
Dependancy to XConfig for transform and save. Could be other library/function
Code: Select all
; PuttingDefaultXML
;**************************
;Compare a Default XML String or Object to an Existing XML Object and add absent nodes to the ExistingXML.
;Save back the Existing XML to ExistingXMLPathToSave
XMLFCT_PuttingDefaultXML(DefaultXmlStrngOrObject,ByRef ExistingXMLObject, ExistingXMLPathToSave)
{
if (ExistingXMLPathToSave=DefaultXmlStrngOrObject)
return
DefaultXMLObject := new Xconfig(DefaultXmlStrngOrObject)
;msgbox Start_PuttingDefaultXML
;------------ nodes--------------
n:=DefaultXMLObject.SelectNodes("//*")
count=0 ; how many changes
while,Node:=n.item[A_Index-1]
{
Node_Name:=Node.nodeName
Node_ParentName:=Node.ParentNode.nodeName
if Node_Name=ROOT
continue
t := ExistingXMLObject.selectSingleNode("//" . Node_ParentName . "/" . Node_Name)
if !IsObject(t) ;If absent from the 2nd file
{
; msgbox % """//" . Node_ParentName . "/" . Node_Name . """ is absent in " . ExistingXMLPathToSave
count++
AbsentNode:=Node.nodeName
p:=Node.cloneNode(true)
y:=ExistingXMLObject.selectSingleNode("//" . Node_ParentName)
y.appendChild(p)
;You can enable following line to add an "added" attribute when the node has been added by the function
;ExistingXMLObject.selectSingleNode("//" . Node_ParentName . "/" . Node_Name).setAttribute("added","1")
}
}
If count<>0
{
ExistingXMLObject.transformXML()
ExistingXMLObject.saveXML()
}
}
Compare a Default XML String or Object to an Existing XML Object and add absent nodes to the ExistingXML.
Dependancy to XConfig for transform and save. Could be other library/function
Code: Select all
; StripIfNotInDefaultXML
;**************************
;Compare a Default XML String or Object to an Existing XML Object and Strip/delete nodes which were not in the Default XML.
;Save back the Existing XML to ExistingXMLPathToSave
XMLFCT_StripIfNotInDefaultXML(DefaultXmlStrngOrObject,ByRef ExistingXMLObject, ExistingXMLPathToSave)
{
if (ExistingXMLPathToSave=DefaultXmlStrngOrObject)
return
DefaultXMLObject := new xml(DefaultXmlStrngOrObject)
;------------ nodes--------------
n:=ExistingXMLObject.SelectNodes("//*")
count=0 ; how many changes
while,Node:=n.item[A_Index-1]
{
Node_Name:=Node.nodeName
Node_ParentName:=Node.ParentNode.nodeName
if Node_Name=ROOT
continue
t := DefaultXMLObject.selectSingleNode("//" . Node_ParentName . "/" . Node_Name)
if !IsObject(t) ;If absent from the 2nd file
{
count++
; msgbox % """//" . Node_ParentName . "/" . Node_Name . """ is absent from your default XML file and so is deleted from the Existing XML
Node.ParentNode.RemoveChild(Node)
}
}
If count<>0
{
ExistingXMLObject.transformXML()
ExistingXMLObject.saveXML()
}
}