Code archives/File Utilities/XML (Load\Save)

This code has been declared by its author to be Public Domain code.

Download source code

XML (Load\Save) by Proger2005
XML.bmx
Import "bbtype.bmx"


Global sdXMLattr_list:TList=New TList
Global sdXMLnode_list:TList=New TList
Global sdXMLworklist_list:TList=New TList
Global sdXMLnodelist_list:TList=New TList
' XML load / parse / save functions
' XML code by Blitztastic

Type sdXMLnodelist Extends TBBType


	Method New()
		Add(sdXMLnodelist_list)		
	End Method

	Method After:sdXMLnodelist()
		Local t:TLink
		t=_link.NextLink()
		If t Return sdXMLnodelist(t.Value())
	End Method

	Method Before:sdXMLnodelist()
		Local t:TLink
		t=_link.PrevLink()
		If t Return sdXMLnodelist(t.Value())
	End Method

	Field node:sdXMLnode
	Field nextnode:sdXMLnodelist
	Field prevnode:sdXMLnodelist
End Type

' for internal use, do not use in code outside of this file
Type sdXMLworklist Extends TBBType

	Method New()
		Add(sdXMLworklist_list)
	End Method

	Method After:sdXMLworklist()
		Local t:TLink
		t=_link.NextLink()
		If t Return sdXMLworklist(t.Value())
	End Method

	Method Before:sdXMLworklist()
		Local t:TLink
		t=_link.PrevLink()
		If t Return sdXMLworklist(t.Value())
	End Method

	Field node:sdXMLnode
End Type

Type sdXMLnode Extends TBBType

	Method New()
		Add(sdXMLnode_list)
	End Method

	Method After:sdXMLnode()
		Local t:TLink
		t=_link.NextLink()
		If t Return sdXMLnode(t.Value())
	End Method

	Method Before:sdXMLnode()
		Local t:TLink
		t=_link.PrevLink()
		If t Return sdXMLnode(t.Value())
	End Method

	Field tag$,value$,path$
	Field firstattr:sdXMLattr
	Field lastattr:sdXMLattr	
	Field attrcount,fileid
	Field endtag$
	
	' linkage functionality
	Field firstchild:sdXMLnode
	Field lastchild:sdXMLnode
	Field childcount
	Field nextnode:sdXMLnode
	Field prevnode:sdXMLnode
	Field parent:sdXMLnode
End Type

Type sdXMLattr Extends TBBType

	Method New()
		Add(sdXMLattr_list)
	End Method

	Method After:sdXMLattr()
		Local t:TLink
		t=_link.NextLink()
		If t Return sdXMLattr(t.Value())
	End Method

	Method Before:sdXMLattr()
		Local t:TLink
		t=_link.PrevLink()
		If t Return sdXMLattr(t.Value())
	End Method

	Field name$,value$
	Field sibattr:sdXMLattr
	Field parent:sdXMLnode
End Type

Global SDXMLFILEID

Function sdReadXML:sdXMLnode(FileName$)
	infile = ReadFile(FileName$)
	SDXMLFILEID=MilliSecs()
	x:sdXMLnode = sdXMLReadNode(infile,Null)
	CloseFile infile
	Return x
End Function

Function sdWriteXML(FileName$,node:sdXMLnode,writeroot=False)
	outfile = WriteFile(FileName$)
	WriteLine outfile,"<?xml version="+Chr$(34)+"1.0"+Chr$(34)+" ?>"
	sdXMLwriteNode(outfile,node)
	CloseFile outfile
End Function

Function sdXMLOpenNode:sdXMLnode(parent:sdXMLnode,tag$="")
	'DebugLog "Opening new node"
	x:sdXMLnode = New sdXMLnode
	x.tag$=tag$
	x.fileid = SDXMLFILEID' global indicator to group type entries (allows multiple XML files to be used)
	sdXMLaddNode(parent,x)
	Return x
End Function

Function sdXMLCloseNode:sdXMLnode(node:sdXMLnode)
	'DebugLog "Closing node ["+node\tag$+"]"
	If node.parent <> Null Then
		'DebugLog "Returning to parent ["+node\parent\tag$+"]"
	Else
		'DebugLog "No Parent found"
	End If
	Return node.parent
End Function

' adds node to end of list (need separate function for insert, or mod this on)
Function sdXMLAddNode(parent:sdXMLnode,node:sdXMLnode)
	If parent <> Null
		'DebugLog "Parent of node = ["+parent\tag$+"]"
		If parent.childcount = 0 Then
			parent.firstchild = node
		Else
			parent.lastchild.nextnode = node
		End If
		node.prevnode = parent.lastchild
		parent.lastchild = node
		parent.childcount = parent.childcount +1
		node.path$ = parent.path$+parent.tag$
	End If
	node.parent = parent
	node.path$=node.path$+"/"
	'DebugLog "path to ["+node\tag$+"]={"+node\path$+"}"
End Function

Function sdXMLDeleteNode(node:sdXMLnode)
	n:sdXMLnode = node.firstchild
	' delete any children recursively
	While n <> Null
		nn:sdXMLnode= n.nextnode
		sdXMLdeletenode(n)
		n = nn
	Wend

	' delete attributes for this node
	a:sdXMLattr = node.firstattr
	While a <> Null
		na:sdXMLattr = a.sibattr
		a.Remove()
		a = na
	Wend

	' dec parents child count
	If node.parent <> Null
		node.parent.childcount = node.parent.childcount -1
		
		' heal linkages
		If node.prevnode <> Null Then node.prevnode.nextnode = node.nextnode
		If node.nextnode <> Null Then node.nextnode.prevnode = node.prevnode
		If node.parent.firstchild = node Then node.parent.firstchild = node.nextnode
		If node.parent.lastchild = node Then node.parent.lastchild = node.prevnode
	End If
	' delete this node		
'	;Debuglog "DELETING:"+node\tag$
	node.Remove()

End Function

' node functions
Function sdXMLfindNode:sdXMLnode(node:sdXMLnode,path$)
	'DebugLog "------------- Perfoming Find ("+path$+")------------"

	ret:sdXMLnode = Null
	p=Instr(path$,"/")
	If p > 0 Then 
		tag$=Left$(path$,p-1)
		';DebugLog "Looking for ["+tag$+"]"
		a:sdXMLnode = node
		While ret=Null And a<>Null 
			';DebugLog "Checking...["+a\tag$+"]"
			If Lower(tag$)=Lower(a.tag$) Then
				If p=Len(path$) Then
						';Debuglog "Found..."
						ret = a
				Else
					If a.firstchild <> Null Then
						ret = sdxmlfindnode(a.firstchild,Mid$(path$,p+1))
					End If
				End If
			End If
			a = a.nextnode
		Wend
	End If
	Return ret
End Function

Function sdXMLDeleteList(nl:sdXMLnodelist)
	While nl <> Null
		na:sdXMLnodelist = nl.nextnode
		nl.Remove()
		nl = na
	Wend
End Function

Function sdXMLSelectNodes:sdXMLnodelist(node:sdXMLnode,path$,recurse=True)
	root:sdXMLnodelist=Null
	sdxmlselectnodesi(node,path$,recurse)
	prev:sdXMLnodelist=Null
	c = 0
	For wl:sdXMLworklist = EachIn sdxmlworklist_list
		c = c + 1
		nl:sdXMLnodelist = New sdXMLnodelist
		nl.node = wl.node
		If prev = Null Then 
			root = nl
			prev = nl
		Else
			prev.nextnode = nl
			nl.prevnode = prev
		End If
		prev = nl
		wl.Remove()
	Next
	'DebugLog "XML: "+c+" nodes selected"
	Return root
End Function

' internal selection function, do not use outside this file
Function sdXMLSelectNodesI(node:sdXMLnode,path$,recurse=True)
	wl:sdXMLworklist=Null
	'DebugLog "------------- Perfoming Select ("+path$+")------------"
	If node = Null Then
		 'DebugLog "Search node is null!!!"
	End If
	ret:sdXMLnode = Null
	p=Instr(path$,"/")
	If p > 0 Then 
		tag$=Left$(path$,p-1)
		a:sdXMLnode = node
		While a<>Null 
			'DebugLog "Looking for {"+path$+"} in {"+a\path$+a\tag$+"/}  {"+Lower(Right$(a\path$+a\tag$+"/",Len(path$)))+"} @"
			If Lower(path$)=Lower(Right$(a.path$+a.tag$+"/",Len(path$))) Then
					wl = New sdXMLworklist
					wl.node = a
					'DebugLog ">>FOUND"
			End If
			If a.firstchild <> Null And (recurse) Then
				sdXMLSelectNodesI(a.firstchild,path$)
			End If
			a = a.nextnode
		Wend
	End If

End Function

Function sdXMLNextNode:sdXMLnode(node:sdXMLnode)
	Return node.nextnode
End Function

Function sdXMLPrevNode:sdXMLnode(node:sdXMLnode)
	Return node.prevnode
End Function

Function sdXMLAddAttr(node:sdXMLnode,name$,value$)
	'DebugLog "XML:adding attribute "+name$+"="+value$+" ("+Len(value$)+")"
	a:sdXMLattr = New sdXMLattr
	a.name$ = name$
	a.value$ = value$
	If node.attrcount = 0 Then
		node.firstattr = a
	Else
		node.lastattr.sibattr = a
	End If
	node.lastattr=a
	node.attrcount = node.attrcount + 1
	a.parent = node
End Function

Function sdXMLReadNode:sdXMLnode(infile,parent:sdXMLnode,pushed=False)
	mode = 0
	root:sdXMLnode = Null
	cnode:sdXMLnode = Null
	x:sdXMLnode = Null
	ispushed = False
	done = False
	While (Not done) & (Not Eof(infile))
		c = ReadByte(infile)
		If c<32 Then c=32
		ch$=Chr$(c)
'		;Debuglog "{"+ch$+"} "+c+" mode="+mode
		Select mode
		  Case 0 ' looking for the start of a tag, ignore everything else
			If ch$ = "<" Then 
				mode = 1' start collecting the tag
			End If
		  Case 1 ' check first byte of tag, ? special tag
		    If ch$ = "?" Or ch$ = "!" Then
		 		mode = 0' class special nodes as garbage & consume
			Else
				If ch$ = "/" Then 
					mode = 2 ' move to collecting end tag
					x.endtag$=ch$
					'DebugLog "** found end tag"
				Else
					cnode=x
					x:sdXMLnode = sdXMLOpennode(cnode)
					If cnode=Null Then root=x
					x.tag$=ch$
					mode = 3 ' move to collecting start tag
				End If
			End If
		  Case 2 ' collect the tag name (close tag)
			If ch$=">" Then 
				mode = 0 ' end of the close tag so jump out of loop
				'done = True
				x = sdXMLclosenode(x)
			Else 
				x.endtag$ = x.endtag$ + ch$
			End If
		  Case 3 ' collect the tag name 
			If ch$=" " Then 
				'DebugLog "TAG:"+x\tag$
				mode = 4 ' tag name collected, move to collecting attributes
			Else 
				If ch$="/" Then 
					'DebugLog "TAG:"+x\tag$
					x.endtag$=x.tag$
					mode = 2' start/end tag combined, move to close
				Else
					If ch$=">" Then
						'DebugLog "TAG:"+x\tag$
						mode = 20' tag closed, move to collecting value
					Else
						x.tag$ = x.tag$ + ch$
					End If
				End If
			End If
		  Case 4 ' start to collect attributes
		    If Lower(ch$)>="a" And Lower(ch$)<="z" Then 
				aname$=ch$'
			    mode = 5' move to collect attribute name
			Else
				If ch$=">" Then
					x.value$=""
					mode = 20' tag closed, move to collecting value
				Else
					If ch$="/" Then 
						mode = 2 ' move to collecting end tag
						x.endtag$=ch$
						'DebugLog "** found end tag"
					End If
				End If
			End If
		  Case 5 ' collect attribute name
		    If ch$="=" Then
			  'DebugLog "ATT:"+aname$
			  aval$=""
			  mode = 6' move to collect attribute value
			Else
			  aname$=aname$+ch$
			End If
		  Case 6 ' collect attribute value
		    If c=34 Then
				mode = 7' move to collect string value
			Else
				If c <= 32 Then 
					'DebugLog "ATV:"+aname$+"="+aval$
					sdXMLAddAttr(x,aname$,aval$)
					mode = 4' start collecting a new attribute
				Else
			   		aval$=aval$+ch$
				End If
			End If
		  Case 7 ' collect string value
			If c=34 Then
				'DebugLog "ATV:"+aname$+"="+aval$
				sdxmlADDattr(x,aname$,aval$)
				mode = 4' go and collect next attribute
			Else
				aval$=aval$+ch$
			End If
		  Case 20 ' COLLECT THE VALUE PORTION
			If ch$="<" Then 
				'DebugLog "VAL:"+x\tag$+"="+x\value$
				mode=1' go to tag checking
			Else
				x.value$=x.value$+ch$
			End If
		End Select
		
		If Eof(infile) Then done=True
	
	Wend

	Return root

End Function

' write out an XML node (and children)
Function sdXMLWriteNode(outfile,node:sdXMLnode,tab$="")
'	;Debuglog "Writing...."+node\tag$+".."
	s$="<"+node.tag$
	a:sdXMLattr = node.firstattr
	While a<>Null
'		;Debuglog "Writing attr ["+a\name$+"]=["+a\value$+"]"
		s$ = s$+" "+Lower(a.name$)+"="+Chr$(34)+a.value$+Chr$(34)
		a = a.sibattr
	Wend
	
	If node.value$="" And node.childcount = 0 Then
		s$=s$+"/>"
		et$=""
	Else
		s$=s$+">"+node.value$
		et$="</"+node.tag$+">"
	End If
	
	WriteLine outfile,sdXMLcleanStr$(tab$+s$)
	n:sdXMLnode = node.firstchild
	While n <> Null
		sdXMLwriteNode(outfile,n,tab$+"  ")
		n = n.nextnode
	Wend
	
	If et$<> "" Then WriteLine outfile,sdXMLcleanStr$(tab$+et$)

End Function

' remove non-visible chars from the output stream
Function sdXMLCleanStr$(s$)
	a$=""
	For i = 1 To Len(s$)
		If Asc(Mid$(s$,i,1))>=32 Then a$ = a$ +Mid$(s$,i,1)
	Next
	Return a$

End Function

' attribute functions
' return an attribute of a given name
Function sdXMLFindAttr:sdXMLattr(node:sdXMLnode,name$)
	ret:sdXMLattr = Null
	If node <> Null Then 
		a:sdXMLattr = node.firstattr
		done = False
		While ret=Null And a<>Null 
			If Lower(name$)=Lower(a.name$) Then
				ret = a
			End If
			a = a.sibattr
		Wend
	End If
	Return ret
End Function

' return an attribute value as a string
Function sdXMLAttrValueStr$(node:sdXMLnode,name$,dflt$="")
	ret$=dflt$
	a:sdXMLattr = sdXMLfindattr(node,name$)
	If a <> Null Then ret$=a.value$
	Return ret$
End Function

' return an attribute value as an integer
Function sdXMLAttrValueInt(node:sdXMLnode,name$,dflt=0)
	ret=dflt
	a:sdXMLattr = sdXMLfindattr(node,name$)
	If a <> Null Then ret=Int(a.value)
	Return ret
End Function

' return an attribute value as a float
Function sdXMLAttrValueFloat#(node:sdXMLnode,name$,dflt#=0)
	ret#=dflt#
	a:sdXMLattr = sdXMLfindattr(node,name$)
	If a <> Null Then ret#=Float(a.value)
	Return ret
End Function

Function XMLValue$(node:sdXMLnode,path$)
	Local t:sdXMLnode=XMLFindNode2(node,path$)
	If t<>Null Then Return t.value$
End Function

Function XMLValueInt(node:sdXMLnode,path$)
	Local t:sdXMLnode=XMLFindNode2(node,path$)
	If t<>Null Then Return Int(t.value$)
End Function

Function XMLValueFloat(node:sdXMLnode,path$)
	Local t:sdXMLnode=XMLFindNode2(node,path$)
	If t<>Null Then Return Float(t.value$)
End Function

Function XMLParam$(node:sdXMLnode,param$)
	Return sdXMLAttrValueStr(node,param$)
End Function

Function XMLParamStr$(node:sdXMLnode,path$,param$)
	Local t:sdXMLnode=XMLFindNode2(node,path$)
	If t<>Null Then Return sdXMLAttrValueStr(t,param$)
End Function

Function XMLParamInt(node:sdXMLnode,path$,param$)
	Local t:sdXMLnode=XMLFindNode2(node,path$)
	If t<>Null Then Return sdXMLAttrValueInt(t,param$)
End Function

Function XMLParamFloat#(node:sdXMLnode,path$,param$)
	Local t:sdXMLnode=XMLFindNode2(node,path$)
	If t<>Null Then Return sdXMLAttrValueFloat(t,param$)
End Function

Function XMLfindNode2:sdXMLnode(node:sdXMLnode,path$)
	Return sdXMLFindNode(node,node.tag$+"/"+path$)
End Function


'x.sdxmlnode = sdReadXML("test.xml")
'sdwritexml("test2.xml",x)

'f.sdxmlnode = sdxmlfindnode(x,"BB3D/NODE/MESH/")
'If f <> Null Then
'	;Debuglog "FOUND!!!"
'	sdxmldeletenode(f)	
'End If

'sdwritexml("test3.xml",x)

'nl.sdxmlnodelist = sdxmlselectnodes(x,"/VERTEX/POS/")
'While nl <> Null;
'	;Debuglog "Found....."+nl\node\tag$
'	nl=nl\nextnode
'Wend
'sdxmldeleteList(nl);



'sdxmldeletenode(x)

Comments

Proger2005
bbtype.bmx
' BBType adds legacy Type functionality to BlitzMax Type

Type TBBType

	Field _list:TList
	Field _link:TLink

	Method Add(t:TList)
		_list=t
		_link=_list.AddLast(Self)
	End Method

	Method InsertBefore(t:TBBType)
		_link.Remove
		_link=_list.InsertBeforeLink(Self,t._link)
	End Method

	Method InsertAfter(t:TBBType)
		_link.Remove
		_link=_list.InsertAfterLink(Self,t._link)
	End Method

	Method Remove()
		_list.remove Self
	End Method

End Type

Function DeleteLast(t:TBBType)
	If t TBBType(t._list.Last()).Remove()
End Function

Function DeleteFirst(t:TBBType)
	If t TBBType(t._list.First()).Remove()
End Function

Function DeleteEach(t:TBBType)
	If t t._list.Clear()
End Function

Function ReadString$(in:TStream)
	Local	length
	length=Readint(in)
	If length>0 And length<1024*1024 Return brl.stream.readstring(in,length)
End Function

Function HandleToObject:Object(obj:Object)
	Return obj
End Function

Function HandleFromObject(obj:Object)
	Local h=HandleToObject(obj)
	Return h
End Function



Code Archives Forum