A
while back, I remember being asked if there was a simple way to expose a source XML document as an object with properties. That is, if the root XML element had a child element <Name>Doug</Name>, then the object would have a Name property that was a string and returned “Doug”. The catch was that the XML document did not conform to a specific schema. Hence, you could not simply create an object with a Name property, because you did not know if the source document had a <Name> element. While there are ways to expose the XML data dynamically, you couldn’t quite do what was being asked.
Enter dynamic objects in Visual Basic 2010. Now, this is possible. This means that you can use the classes in the System.Dynamic namespace to create objects that expose properties and methods dynamically at run-time and solve the original problem. In this example, we will create an object that inherits the System.Dynamic.DynamicObject class. The DynamicObject class has methods that you can override to provide code specific to your implementation. When VB performs a late-bound request on an object that implements the DynamicObject class, it calls one of the methods of the DynamicObject class to obtain the result. Consider this simple example:
Imports System.Dynamic
Public Class SimpleObject
Inherits DynamicObject
Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder,
ByRef result As Object) As Boolean
If binder.Name = “Address” Then
result = “Value”
Return True
End If
Return False
End Function
End Class
Now consider this code that creates an instance of the object and accesses properties of that instance.
Module Module1
Sub Main()
Dim t As Object = New SimpleObject()
Console.WriteLine(t.Address)
Console.WriteLine(t.Street)
End Sub
End Module
First, in order to ensure that requests made of the dynamic object are late-bound, we need to type the variable as Object. Then we make two late-bound requests for object properties: Address and Street. When you access the Address property, a call is made to the TryGetMember method of the DynamicObject class, which we have overridden. The GetMemberBinder object instance that is passed to our TryGetMember method contains the name of the requested member in its Name property. Our small sample matches that name and returns the string “Value”. When you access the Street property, a call is made to the TryGetMember method, the name does not match anything that our code supports, so the method returns False. False indicates an unsupported member, and an exception is thrown.
To solve our initial problem of “hiding” source XML and exposing an object instead, we can use the DynamicObject class in this same fashion. When a property is requested of our dynamic object, our code can search the XML source for a matching XML element name, and return the corresponding value as the property value. Let’s look at an example.
DynamicXmlObject example
Let’s jump into the code and we’ll talk about how the object behaves as we go. We start with a class that inherits DynamicObject. Let’s name it DynamicXmlObject.
Imports System.Dynamic
Public Class DynamicXmlObject
Inherits DynamicObject
End Class
Constructors
We will add two constructors for the class. One that takes a path to an XML file as the source, and another that takes an XElement. The constructor that takes an XElement is not just for user convenience. We’ll use this later when dealing with child elements that have attributes or children. Also for this class, we won’t expose a settable property for the source XML, so we’ll “disable” the empty constructor by making it Protected.
Imports System.Dynamic
Imports System.IO
Public Class DynamicXmlObject
Inherits DynamicObject
‘ The source XML for this instance.
Private p_Xml As XElement
‘ Create a new DynamicXmlObject with the specified source file.
Public Sub New(ByVal filePath As String)
If Not File.Exists(filePath) Then
Throw New Exception(“File does not exist.”)
End If
p_Xml = XElement.Load(filePath)
End Sub
‘ Create a new DynamicXmlObject with the specified source XElement.
Public Sub New(ByVal xml As XElement)
p_Xml = xml
End Sub
‘ Disable the empty constructor.
Protected Sub New()
End Sub
End Class
Implementing GetDynamicMemberNames
This next piece of code is optional. I’ve added it so that our object supports reflection-like functionality. You can expose the member names of your dynamic object using the GetDynamicMemberNames method of the DynamicObject class. Our sample object will return all of the child element names and attribute names of the current element. I’ve added a case-insensitive Distinct comparison so that only a single name is returned if multiple entries are found. The search looks at only the LocalName property of the XName for an element or attribute. XML namespaces are ignored. That is, <a:Name> and <b:Name> are considered to be duplicate element names.
Public Overrides Function GetDynamicMemberNames() As IEnumerable(Of String)
Dim names = (From e In p_Xml.Elements() Select e.Name.LocalName).Union(
From a In p_Xml.Attributes() Select a.Name.LocalName)
Return (From n In names
 
0 comments
Be the first to start the discussion.