Sign in to follow this  
Followers 0

XML Functions for AutoIt

12 posts in this topic

Posted

If there's another post with something like this, please just say so. I HAVE searched the forums, and haven't seen anything like this yet.

There has been a lot of talk about XML support for AutoIt, and even some attempts at creating XML functions. However, most have been related to specific XML files rather than generic utility functions to work with ANY XML file.

In his post under _GetIP - AutoIt Forums, MSLx Fanboy dropped this tidbit of code for getting an IP address:

Func GetIP()

Local $xmldoc = ObjCreate('Microsoft.XMLDOM'), $ip

If Not IsObj($xmldoc) Then Return -1

$xmldoc.async = False

$xmldoc.load ('http://xml.showmyip.com/')

For $x In $xmldoc.documentElement.childNodes

If $x.NodeName = "ip" Then

$ip = $x.text

ExitLoop

EndIf

Next

$xmldoc = 0

Return $ip

EndFunc

This inspired me to see if more generic functions could be created using the Microsoft.XMLDOM object. I've been able to make a start of it with the following function:

;===============================================================================
;
; Function Name:	_XML_ElementsToArry()
; Description:	  Return an array of the elements of an XML file
; Parameter(s):	 $sXMLFilePath	- String with full path to XML file
; Requirement(s):   AutoIt 3.1.1.53 Beta or better
; Return Value(s):  On Success - Returns an array. Element[0] contains the
;								 number of elements found, the remaining
;								 array contains the names of the elements
;				   On Failure - returns -1 and sets @error to 1
; Author(s):		JerryD
;
;===============================================================================
Func _XML_ElementsToArry ( $sXMLFilePath )
	Local $i = 1
	Local $aRetAry[1]
	$aRetAry[0] = 0
	Local $xmldoc = ObjCreate( 'Microsoft.XMLDOM' )
	If Not IsObj($xmldoc) Then
		SetError ( 1 )
		Return -1
	EndIf
	$xmldoc.async = False
	$xmldoc.load ( $sXMLFilePath )
	For $x In $xmldoc.documentElement.childNodes
		$aRetAry[0] = $aRetAry[0] + 1
		ReDim $aRetAry[$aRetAry[0]+1]
		$aRetAry[$i] = $x.NodeName
		$i = $i + 1
	Next
	Return $aRetAry
EndFunc
I've run the code against two sample XML files (attached), and it seems to be working well and returning the correct information.

Where I've run into a brick wall is trying to decipher the "documentation" at MSDN on how to use the Microsoft.XMLDOM object. I'm hoping someone here can help expand on this function to be able to easily extract data from ANY XML file.

For starters, once you have the "elements" of an XML document, how do you access the details?

If you look at IbmEgath.XML, the structure is very simple. There are elements and details under each element. However, if you look at WSUS_Scan_3679.xml, the function above returns only one element (Check ID), then there's a Detail element under that, and then elements under Details that contain the data.

While I understand you wouldn't write a script to gather data from some random XML file whose structure is unknown, it seems like there would be a way to write some generic functions that could be used to access XML data more easily.

Any takers?

IbmEgath.XML

WSUS_Scan_3679.xml

Share this post


Link to post
Share on other sites



Posted

I have some experience with XML and quite a lot with navigating MSDN. I don't have pat answers for you, but here are some thoughts...

First, I applaud your efforts here -- some generalized XML UDFs would be great to have.

I realize you're just dipping your toe in the water, but I believe that trying to convert XML into an array in AutoIt will bring you only grief. The data structure in an arbitrary XML file is, well, arbitrary. Occasionally, it may be defined in a way that lends itself to representation in a structured array, but just as often the data will be hierarchical (i.e. with nested arrays of elements to arbitrary depths) and with arbitrary attributes defined.

I say all of that to make the argument that a better approach would be to create routines that make it easier to access the XML in its native format through AutoIt rathter than focus on converting the XML into more traditional data structures inside AutoIt.

You may have found these pointers already in MSDN, but if not, here are some starting points. I also recommend looking for VBScript examples as they are very easy to convert to AutoIt:

A Beginner's Guide to the XML DOM

Microsoft COM implementation of the XML Document Object Model (DOM)

XML DOM Objects/Interfaces

Dale

Share this post


Link to post
Share on other sites

Posted

If only I had more time, it would be nice to build a function library for xml, much like that made for IE

Share this post


Link to post
Share on other sites

Posted

Come to me, and I will give thee a plugin.

Trust in me, and an XML engine will be made for you.

This I promise you, as an XML intepreter would be easy as hell.

Share this post


Link to post
Share on other sites

Posted

You may have found these pointers already in MSDN, but if not, here are some starting points. I also recommend looking for VBScript examples as they are very easy to convert to AutoIt:

A Beginner's Guide to the XML DOM

Microsoft COM implementation of the XML Document Object Model (DOM)

XML DOM Objects/Interfaces

Dale

Thanks for the links DaleHohm.

The Beginner's Guide to the XML DOM (XML Developer's Center) link is new to me, and I'll try going through that more, but I've been through the other two.

I'm a self taught scripter (not programmer). I used to program in C years ago, but that was long before Object Oriented programming and I get totally lost in how the elements are are addressed. I only recently realized that METHODS are ways to manipulate elements within the objects, so although progress is slow, I am making some headway.

I don't discount your other advice either. Thanks again for your input.

Share this post


Link to post
Share on other sites

Posted

As DaleHohm said convert XML to array is not right in general way.

If you really need convert XML to something, it should be rather TreeView...

Share this post


Link to post
Share on other sites

Posted

As DaleHohm said convert XML to array is not right in general way.

If you really need convert XML to something, it should be rather TreeView...

Using TreeView sounds great. Show me!

Displaying the data isn't the point - the point is GETTING the data to display in the first place!

As I've said, I have NO training in programming. I only know what I've learned through trial and error over 25 years working with computers. The reason it seemed logical to me to load what I call Elements into an array is because it looked like those values would be useful in other routines.

If you look at IbmEgath.XML, each "element" (EG_GATHERER_VERSION, EG_SYSTEM_SUMMARY, etc.) has 1 or more pieces of data in it. The function I wrote loads those "element" values into an array which I assume could be used in other function calls to gather the data within those elements. What I haven't figured out is how to get that info using 'Microsoft.XMLDOM'.

In WSUS_Scan_3679.xml, only ONE "element" is returned by my function. ( Check ). I assume this value would be needed to recurse further into the XML file to get "sub elements" which ultimately hold the data one would be looking for. Again, I have no idea how to use 'Microsoft.XMLDOM' to dig deeper into the XML file.

Anyway, that's just an explanation of what and why I was looking in this direction. If you have other solutions, fine! I'm not wed to the one sole function I've created, but some sample code and/or more insight on how to work with the XML data is what I'm looking for.

Thanks.

Share this post


Link to post
Share on other sites

Posted

The problem with loading something stored as a tree (XML's structure is a tree) into an array is that you lose lineage. It becomes very difficult to determine whether a node is a child of another node or to find the parent of a node, et cetera. Arrays are only suited for a very small percentage of XML documents and it is not a general case storage container.

Share this post


Link to post
Share on other sites

Posted (edited)

Here's a script for showing XML in a treeview. It is only prelim but it seems to work ok.

#include <GUIConstants.au3>
#Include <GuiTreeView.au3>
#include <Array.au3>
#include <GuiMenu.au3>;; unoffical UDF see post http://www.autoitscript.com/forum/index.php?showtopic=6577#
; ------------------------------------------------------------------------------
;_xmlTreeView
; AutoIt Version: 	3.0
; Language: 		English
; Description: 		view XML file in treeview
;					Stephen Podhajecki <gehossafats@netmdc.com>
; Dec 29, 2005
; Requires Beta version
; ------------------------------------------------------------------------------

;===============================================================================
; Constants and Global Variables
Global Const $NODE_ELEMENT = 1;
Global Const $NODE_ATTRIBUTE = 2;
Global Const $NODE_TEXT = 3;
Global Const $NODE_CDATA_SECTION = 4;
Global Const $NODE_ENTITY_REFERENCE = 5;
Global Const $NODE_ENTITY = 6;
Global Const $NODE_PROCESSING_INSTRUCTION = 7;
Global Const $NODE_COMMENT = 8;
Global Const $NODE_DOCUMENT = 9;
Global Const $NODE_DOCUMENT_TYPE = 10;
Global Const $NODE_DOCUMENT_FRAGMENT = 11;
Global Const $NODE_NOTATION = 12;
Global $strFile;xml filename
Global $aRecent[6];recent files menu
Global $aMenu[6];recent files holder
Global $oOXml =""
;===============================================================================

;===============================================================================
; Create Gui
$GUI = GUICreate("XML TreeView")
$filemenu = GUICtrlCreateMenu("&File")
$fileOpen = GUICtrlCreateMenuItem("Open", $filemenu)
$fileClose = GUICtrlCreateMenuItem("Close", $filemenu)
GUICtrlSetState(-1, $GUI_DISABLE)
$helpmenu = GUICtrlCreateMenu("Info")
;$saveitem = GUICtrlCreateMenuitem ("Save",$filemenu)
;GUICtrlSetState(-1, $GUI_DISABLE)
$infoitem = GUICtrlCreateMenuItem("Info", $helpmenu)

;~ $delitem = GUICtrlCreateMenuItem("Del", $helpmenu

$exititem = GUICtrlCreateMenuItem("Exit", $filemenu)
$separator1 = GUICtrlCreateMenuItem("", $filemenu, 2); create a separator line

$recentfilesmenu = GUICtrlCreateMenu("Recent Files", $filemenu)
GUICtrlSetState(-1, $GUI_DISABLE)

$viewmenu = GUICtrlCreateMenu("Options", -1, 1); is created before "?" menu
$viewinline = GUICtrlCreateMenuItem("Show Attribs Inline", $viewmenu)
GUICtrlSetState(-1, $GUI_CHECKED)

$hTree = GUICtrlCreateTreeView(5, 5, 390, 350)
GUISetState()
_Persist("load")
;===============================================================================

;===============================================================================
; Main GUI msg loop

While 1
	$msg = GUIGetMsg()
	Select
		Case $msg = $fileOpen
;$sXmlFile = FileOpenDialog("XML Tree View", @MyDocumentsDir, "XML (*.xml)", 1, "C:\mount.xml")
			$sXmlFile = FileOpenDialog("XML Tree View", @ProgramFilesDir & "\autoit\scrpits", "XML (*.xml)", 1)
			If @error Then
				MsgBox(4096, "File Open", "No file chosen")
			Else
				$oOXml = ""
				$oOXml = _XmlFileLoad($sXmlFile)
				_UpdateMenu($sXmlFile)
				_XmlBuildTree($oOXml)
				
			EndIf
		Case $msg = $fileClose
			GUICtrlDelete($hTree)
			GUICtrlSetState($fileClose, $GUI_DISABLE)
			$oOXml =""
		Case $msg = $viewinline
			If _Inline() = $GUI_CHECKED Then
				GUICtrlSetState($viewinline, $GUI_UNCHECKED)
				if isObj($oOXml) then 	_XmlBuildTree($oOXml)
			Else
				GUICtrlSetState($viewinline, $GUI_CHECKED)
			if isObj($oOXml) =  then _XmlBuildTree($oOXml)
			EndIf
			
;~ 		Case $msg = $delitem
;~ 			$mruHwnd = GUICtrlGetHandle ($recentfilesmenu)
;~ 			$mruCount = _GUICtrlMenuGetItemCount ($mruHwnd)
;~ 			_GuiCtrlMenuDeleteItem ($mruHwnd, 0, 1)
;~ 			$mruCount = _GUICtrlMenuGetItemCount ($mruHwnd)
;~ 			ConsoleWrite(@CRLF & "After[" & $mruCount & "]")
		Case $msg = $GUI_EVENT_CLOSE Or $msg = $exititem
			ExitLoop
		Case $msg = $infoitem
			MsgBox(0, "Info", "Loads XML file into tree view" & @CRLF & _
					"Precursor to XML Editor?? Maybe.")
		Case Else
			If not ($msg = 0) Then
				For $c = 0 To UBound($aRecent) - 1
					If $msg = $aRecent[$c] Then
						$menuitem = GUICtrlRead($aRecent[$c], 1)
						If not ($menuitem = 0) Then
				;MsgBox(0, "Name of selected file", $menuitem[0])
							$oOXml = _XmlFileLoad($menuitem[0])
							_XmlBuildTree($oOXml)
						EndIf
					EndIf
				Next
			EndIf
;			If $msg <> 0 And $msg <> - 11 Then ConsoleWrite($msg & " ")
;;
	EndSelect
WEnd
_Persist("save")
;===============================================================================
GUIDelete()
Exit

;===============================================================================
;Helper Functions
;===============================================================================
;===============================================================================
; Function Name:	 _UpdateMenu
; Description:		Updates the recent files submenu
; Parameters:
; Syntax:			 _UpdateMenu()
; Author(s):		Stephen Podhajecki <gehossafats@netmdc.com>
; Returns:			none
;===============================================================================
Func _UpdateMenu($strNewFile)
	if $strNewFile <> "" and $strNewFile <>"0"Then
	$bExists =False
	GUICtrlSetState($recentfilesmenu, $GUI_ENABLE)
	$mruHwnd = GUICtrlGetHandle($recentfilesmenu)
	$mruCount = _GUICtrlMenuGetItemCount ($mruHwnd)
	If $mruCount < 1 Then
		GUICtrlSetState($recentfilesmenu, $GUI_ENABLE)
	EndIf
	While $mruCount > 5
		_GuiCtrlMenuDeleteItem ($mruHwnd, 0, 1)
				$mruCount = _GUICtrlMenuGetItemCount ($mruHwnd)
	WEnd
	For $x = 0 To UBound($aRecent) - 1
		$sMenuItem = GUICtrlRead($aRecent[$x], 1)
		if $strNewFile = string($sMenuItem[0]) Then
			$bExists = True
			ExitLoop
		EndIf
	Next
	If $bExists = False Then _ArrayPush ($aRecent, GUICtrlCreateMenuItem($strNewFile, $recentfilesmenu))
	EndIf
	
EndFunc;==>_UpdateMenu

;===============================================================================
; Function Name:	 _DisplayNode
; Description:		build treeview from xml.
; Parameters:		$xml_mode		the xmlnode
;					$treeview_node	where to add the node in the treeview
; Syntax:			 _XMLFileOpen($str)
; Author(s):		Stephen Podhajecki <gehossafats@netmdc.com>
; Returns:			none
;===============================================================================

Func DisplayNode($objNode, $tvParentNode)
	Local $nodetype, $atribtype, $flag, $str_nodx
	$blnAttribNodes = (_Inline() And $GUI_CHECKED = 1)
;	ConsoleWrite($blnAttribNodes)
	$nodetype = $objNode.nodetype
	Select
		
		Case $nodetype = $NODE_ELEMENT
			$nodx = GUICtrlCreateTreeViewItem($objNode.baseName, $tvParentNode)
			
		Case $nodetype = $NODE_COMMENT
			$nodx = GUICtrlCreateTreeViewItem("* " & $objNode.Text & " *", $tvParentNode)
		Case $nodetype = $NODE_CDATA_SECTION
			$nodx = GUICtrlCreateTreeViewItem("~ " & $objNode.Text & " ~", $tvParentNode)
			
		Case $nodetype = $NODE_TEXT
			$nodx = GUICtrlCreateTreeViewItem($objNode.Text, $tvParentNode)
			
		Case Else
			$nodx = $tvParentNode
	EndSelect
	
	If IsObj($objNode.Attributes) Then
		For $atrib In $objNode.Attributes
			$atribtype = $atrib.nodeType
			Select
				
				Case $atribtype = $NODE_ATTRIBUTE
					If Not $blnAttribNodes Then
						$noda = GUICtrlCreateTreeViewItem("[" & $atrib.baseName & "=" & $atrib.Value & "]", $nodx)
						
					Else
						$str_nodx &= "[" & $atrib.baseName & "=" & $atrib.Value & "] "
						$flag = True
					EndIf
					
				Case $atribtype = $NODE_PROCESSING_INSTRUCTION
					$noda = GUICtrlCreateTreeViewItem("{{" & $atrib.baseName & "=" & $atrib.Value & "}}", $nodx)
			EndSelect
		Next
		If $flag = True Then
			$noda = GUICtrlCreateTreeViewItem($str_nodx, $nodx)
			$flag = False
		EndIf
		
	EndIf
	
	If $objNode.childNodes.length > 0 Then
		For $nod In $objNode.childNodes
			DisplayNode($nod, $nodx)
		Next
	EndIf
EndFunc;==>DisplayNode

;===============================================================================
; Function Name:	 _XmlBuildTree
; Description:		build treeview from xml.
; Parameters:		$objXml		loaded XML file
;					$treeview 	Treeview control to manipulate
; Syntax:			_XmlBuildTree($objXml,$treeview)
; Author(s):		Stephen Podhajecki <gehossafats@netmdc.com>
; Returns:
;===============================================================================
Func _XmlBuildTree($objXML)
;===============================================================================
;uncomment one of the following blocks, not much difference
;one places the file name in the tree the other doesn't
	
;;========= First node is root node
;$oXmlRoot = $objXml.documentElement
;$oFormItem = GUICtrlCreateTreeViewItem($oXMLRoot, $tview)
	
;;========= First node is Filename
	$oXmlRoot = $objXML
	GUICtrlDelete($hTree)
	$hTree = GUICtrlCreateTreeView(5, 5, 390, 350)
	$oFormItem = GUICtrlCreateTreeViewItem($strFile, $hTree)
	If $oFormItem = 0 Then
		MsgBox(0, "Error", "Failed to Create Treeview Item")
		Return
	EndIf
	
;===============================================================================
	For $oNodes In $oXmlRoot.childNodes
		DisplayNode($oNodes, $oFormItem)
	Next
	GUICtrlSetState($oFormItem, $GUI_FOCUS)
	GUICtrlSetState($fileClose, $GUI_ENABLE)
EndFunc;==>_XmlBuildTree

;===============================================================================
; Function Name:	 _XmlFileLoad
; Description:		Load and parse the XML file
; Parameters:		$strFile	XML file to load
; Syntax:			_XmlBuildTree($objXml,$treeview)
; Author(s):		Stephen Podhajecki <gehossafats@netmdc.com>
; Returns:			Object handle on success
;					0 on failure and sets error to parser error.
;===============================================================================
Func _XmlFileLoad($strXmlFile)
	$strFile = $strXmlFile
;if not isobj($oXml) then
	$oXml = ObjCreate("Msxml2.DOMDocument")
;	$oXml.LoadXML (FileRead($strFile, FileGetSize($strFile)))
	$oXml.load ($strFile)
	If $oXml.parseError.errorCode <> 0 Then
		MsgBox(4096, "Error", "Error opening specified file: " & $strFile & @CRLF & $oXml.parseError.reason)
		SetError($oXml.parseError.errorCode)
		Return 0
	EndIf
	Return $oXml
EndFunc;==>_XmlFileLoad

;===============================================================================
; Function Name:	 _Inline
; Description:		Returns the checked state of inline menu item
; Parameters:		none
; Syntax:			_Inline()
; Author(s):		Stephen Podhajecki <gehossafats@netmdc.com>
; Returns:			Returns the checked state of inline menu item
;===============================================================================
Func _Inline()
	Return BitAND(GUICtrlRead($viewinline), $GUI_CHECKED)
EndFunc;==>_Inline

Func SHAddToRecentDocs($dwData, $dwFlags = 2)
;To add a file to the list of recent documents you just have to pass 2 in the first argument,
;which means that you're passing a string, and pass the file name in the second argument:
;(the value 1 in the first argument means that you're passing a PIDL, which isn't the case.)
;You can also clear the list of recent documents by passing a NULL in the second argument:
	DllCall("shell32.dll", "long", "SHAddToRecentDocs", $dwFlags, "long", $dwData, "str")
EndFunc;==>SHAddToRecentDocs

;===============================================================================
; Function Name:	 _Persist()
; Description:		Saves or load settings to/from ini
; Parameters:		$strFileName
; Syntax:			_Persist($strFileName)
; Author(s):		Stephen Podhajecki <gehossafats@netmdc.com>
; Returns:
;===============================================================================
Func _Persist($command = "")
	$command = StringUpper($command)
	If $command = "" Or $command = "SAVE" Then
		$y = 0
		For $x = 0 To UBound($aRecent) - 1
			$sRecent = GUICtrlRead($aRecent[$x], 1)
			If $sRecent[0] = "0" Or $sRecent[0] = "" Then
			Else
				IniWrite(@ScriptFullPath & ".ini", "Files", $y,($sRecent[0]))
				$y = $y + 1
			EndIf
		Next
		IniWrite(@ScriptFullPath & ".ini", "Files", "files", $y)
		
		IniWrite(@ScriptFullPath & ".ini", "Inline", "Checked", _Inline())
	ElseIf $command = "LOAD" Then
		If FileExists(@ScriptFullPath & ".ini") Then
			$recent = IniRead(@ScriptFullPath & ".ini", "Files", "files", 0)
			For $x = 0 To $recent - 1
				_UpdateMenu(IniRead(@ScriptFullPath & ".ini", "Files", $x, ""))
			Next
			GUICtrlSetState($viewinline, IniRead(@ScriptFullPath & ".ini", "Inline", "Checked", 0))
;			_UpdateMenu()
		EndIf
	EndIf
EndFunc;==>_Persist
If _Inline() = $GUI_CHECKED Then
	GUICtrlSetState($viewinline, $GUI_UNCHECKED)
	_XmlBuildTree($oOXml)
Else
	GUICtrlSetState($viewinline, $GUI_CHECKED)
	_XmlBuildTree($oOXml)
EndIf

Hope it helps. :P

I have a set of DOM wrapper funcs too that I am about to post along with an example for saving settings to an XML file. :lmao:

Edit:

Bug fix: Large XML file would cause tree to fail

Added MRU peristance to INI file

Added Close menu Item

Edit:

Bug fix: Change view before loading file caused crash

Edit:

Attached correct file.

Edit:

Bug fix: When using Open from menu, _UpdateMenu() would go into infinite loop when menu count > 5

Updated version:

xmltree.au3

Edited by eltorro

Share this post


Link to post
Share on other sites

Posted

I get a couple of missing procedures

_GuiCtrlMenuDeleteItem()

_GuiCtrlMenuDeleteItem()

Any thoughts

Share this post


Link to post
Share on other sites

Posted

Nice!

Could you check the link for treexml.au3?

I get a VBScript file with two different browsers. (Could just be because I'm behind a firewall with cache.)

Note. The GuiMenu.au3 is in the topic mentioned in the source code above.

Bill

Share this post


Link to post
Share on other sites

Posted

@steve8tch

reply 9 near the top:

Func _GUICtrlMenuDeleteItem($DMI_hWnd, $DMI_id, $DMI_opt)
  If $DMI_hWnd <= 0 Or $DMI_id < 0 Then Return -1
  $MF_BYCOMMAND = 0x00000000
  $MF_BYPOSITION = 0x00000400
  If $DMI_Opt = 0 Then
	 $DMI_Opt = $MF_BYCOMMAND
  Else
	 $DMI_Opt = $MF_BYPOSITION
  EndIf
  $DMI_r = DllCall("user32.dll","hwnd","DeleteMenu", _
								"hwnd",$DMI_hWnd, _
								"int",$DMI_id, _
								"int",$DMI_opt )
  If @error Then Return 0
  Return $DMI_r[0]
EndFunc

It's in the orig post too.

:P

Share this post


Link to post
Share on other sites
Sign in to follow this  
Followers 0