(c) 2002 Visual Studio Magazine 
Fawcette Technical Publications

VB.NET	Put the Tools Class to Work
Listing 1	The Tools class does the work of merging XML files. It validates the XML document based on a schema, performs an intimate merge combining elements and attributes, and illustrates the use of utility functions to simplify XML processing.

Public Class Tools
	Public Shared Function LoadValidXML( _
		ByVal xmlFile As String, _
		ByVal xsdFile As String, _
		ByVal nSpace As String) _
		As Xml.XmlDocument
		Dim xmlDoc As New Xml.XmlDocument
		Dim rdr As Xml.XmlTextReader
		Dim validRdr As Xml.XmlValidatingReader
		Try
			rdr = New Xml.XmlTextReader(xmlFile)
			validRdr = New _
				Xml.XmlValidatingReader(rdr)
			validRdr.Schemas.Add(nSpace, xsdFile)
			xmlDoc.Load(validRdr)
			Return xmlDoc
			'	Throw
		Finally
			validRdr.Close()
		End Try
	End Function

	Public Shared Sub MergeRoot( _
		ByVal outDoc As Xml.XmlNode, _
		ByVal mergeDoc As Xml.XmlDocument)
		Dim owner As Xml.XmlDocument = _
			outDoc.ChildNodes(0).OwnerDocument
		MergeNode(owner, outDoc, _
			mergeDoc.ChildNodes(1), Nothing)
	End Sub

	Private Shared Sub MergeNode( _
		ByRef owner As _
		Xml.XmlDocument, _
		ByRef outParent As Xml.XmlNode, _
		ByRef node As Xml.XmlNode, _
		ByRef nameAttr As Xml.XmlAttribute)
		Dim nsmgr As Xml.XmlNamespaceManager
		Dim idName As String = node.Name & "ID"
		Dim pred As String = ""
		nsmgr = New _
			Xml.XmlNamespaceManager( _
			owner.NameTable)
		nsmgr.AddNamespace("zzz", _
			node.NamespaceURI)
		If Not nameAttr Is Nothing Then
			pred = "[@" & idName & "='" & _
				nameAttr.Value & "']"
		End If
		Dim test As Xml.XmlNode = _
			outParent.SelectSingleNode( _
			"zzz:" & node.Name & pred, nsmgr)
		If test Is Nothing Then
			outParent.AppendChild( _
				owner.ImportNode(node, True))
		Else
			' Node exists, add attributes, then 
			' children
			For Each attrib As Xml.XmlAttribute In _
				node.Attributes
				If test.Attributes(attrib.Name) Is _
					Nothing Then
					test.Attributes.Append( _
						Tools.NewAttribute( _
						test.OwnerDocument, _
						attrib.Name, attrib.Value))
				Else
					test.Attributes( _
						attrib.Name).Value = _
						attrib.Value
				End If
			Next
			For Each childNode As Xml.XmlNode In _
				node.ChildNodes
				idName = childNode.Name & "ID"
				MergeNode(owner, test, childNode, _
					childNode.Attributes(idName))
			Next
		End If
	End Sub

' Utility Functions follow
	Public Shared Function GetAttributeOrEmpty( _
		ByVal node As Xml.XmlNode, _
		ByVal attrName As String) _
		As String
		Dim attr As Xml.XmlNode = _
			node.Attributes.GetNamedItem(attrName)
		If Not attr Is Nothing Then
			Return attr.Value
		Else
			Return ""
		End If
	End Function

	Public Shared Function NewAttribute( _
		ByVal xmlDoc As Xml.XmlDocument, _
		ByVal name As String, _
		ByVal value As String) _
		As Xml.XmlAttribute
		Dim nodeAttr As Xml.XmlAttribute
		nodeAttr = xmlDoc.CreateAttribute(name)
		nodeAttr.Value = value
		Return nodeAttr
	End Function

End Class
