Working with XML Data Using the XPathNavigator Class in InfoPath 2007 Form Templates
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
Summary: Learn how to use the XPathNavigator and XPathNodeIterator classes to access and work with data in business logic written with managed code in Microsoft Office InfoPath 2007 form templates. (18 printed pages)
Mark Roberts, Microsoft Corporation
Applies to: Microsoft Office InfoPath 2007, InfoPath Forms Services 2007
One of the main design principles for Microsoft Office InfoPath 2007 and InfoPath Forms Services 2007 is that you can create your form template once, write code for it once, build and publish it once (to a server running InfoPath Forms Services, such as Microsoft Office Forms Server 2007 and Microsoft Office SharePoint Server 2007), and then have it work in both the browser and InfoPath 2007. The InfoPath product team refers to this design principle as "design once." To make "design once" easier for form templates that contain business logic code, a new managed code object model was created. It is provided by the types and members of the Microsoft.Office.InfoPath namespace.
To support the ability to access and manipulate the XML data in form template data sources regardless of whether the form template is open in InfoPath or in the browser, many InfoPath managed code object model members either create, or can be passed, an instance of the XPathNavigator class of the System.Xml.XPath namespace. Additionally, some object model members return an XPathNodeIterator that enables you to iterate over a collection of XML nodes.
After you have access to an XPathNavigator object or an XPathNodeIterator object returned by an InfoPath object model member, you can use the properties and methods of these classes to work with the form's data and secondary data sources.
For a complete listing of the classes and members in the Microsoft.Office.InfoPath namespace that can use the XPathNavigator and XPathNodeIterator classes, see the Appendix: InfoPath Object Model Members That Work with XPathNavigator and XPathNodeIterator section in this article.
This article provides examples of how to work with XML data using the XPathNavigator and XPathNodeIterator objects from managed code in InfoPath 2007 form templates.
One commonly used member of the Microsoft.Office.InfoPath namespace that uses the XPathNavigator class is the CreateNavigator() method of the DataSource class. The CreateNavigator method creates an XPathNavigator object positioned at the root of the data source represented by the specified DataSource object. You can use the CreateNavigator method to access the main data source, which is the underlying XML document of the form. You can also access any secondary data sources that are defined using the Data Connections command on the Tools menu, such as a separate XML document, database, Web service, or SharePoint list.
Accessing the Main Data Source
The MainDataSource() property of the XmlForm class gets a reference to a DataSource object that represents the underlying XML document of the form. You can then use the CreateNavigator method of the DataSource object to create an XPathNavigator object to work with the underlying XML data in the form.
In InfoPath 2007, you can access the XmlForm object for the current form by using the this keyword (C#) or the Me keyword (Visual Basic). The following example uses the MainDataSource property to access the DataSource object that represents the underlying XML document of the form. It then uses the CreateNavigator method to create an XPathNavigator object positioned at the root of the data source. This XPathNavigator object is assigned to the myNavigator variable, and the OuterXml property of the XPathNavigator class is used to display the XML of the main data source in a message box.
Dim myNavigator As XPathNavigator = _ Me.MainDataSource.CreateNavigator() MessageBox.Show(myNavigator.OuterXml.ToString(),"Main Data Source XML")
InfoPath does not require that you use the this keyword (C#) or the Me keyword (Visual Basic) to access members of the XmlForm object for the current form. You can omit them from your code.
Accessing a Secondary Data Source
The following example assumes that you used the Data Connections command to create a secondary data source that contains a list of city names and named it "CityList." The kind of data source that you are connecting to is not necessarily important. The important detail is that you are accessing the data source by referencing the name—in this case, "CityList"—from the DataSourceCollection object returned by the DataSources() property of the XmlForm object for the current form. After the XPathNavigator object is created for the "CityList" data source using the CreateNavigator method, it is assigned to the myNavigator variable, and the OuterXml property of the XPathNavigator class is used to display the XML of the data source in a message box.
To select a single node in a data source, use the SelectSingleNode(String,IXmlNamespaceResolver) method of the XPathNavigator class. If you want to work with a set of repeating fields or repeating groups, use the Select(String,IXmlNamespaceResolver) method of the XPathNavigator class. This method returns an XPathNodeIterator object that represents a collection of nodes.
Selecting a Single Node
The overloaded SelectSingleNode method that you must use has an xpath parameter that takes an XPath expression as a string, and a resolver parameter that takes an XmlNamespaceManager object for resolving namespace prefixes. To select a single node in the form's main data source, pass in an XPath expression that specifies the field or group you want to select for the xpath parameter, together with the XmlNamespaceManager object that is returned by the NamespaceManager() property of the XmlForm object. The returned XmlNamespaceManager object is initialized at load time with all the namespaces that are defined by the xmlns attributes in the xDocumentClass element of the form template's form definition file (.xsf).
The easiest way to create an XPath expression for selecting a node in the form's data source is to right-click the field or group in the Data Source task pane, and then click Copy XPath. To create and test more complex manually edited XPath expressions, add an Expression Box control to the form, specify the XPath expression for the control, and then preview the form to display the results.
The following example uses the SelectSingleNode method to select the node for the EmailAlias field. Then it uses the SetValue method of the XPathNavigator class and the UserName() property of the User class to set the value of the field to the current user's alias.
Dim emailAlias As XPathNavigator = _ Me.MainDataSource.CreateNavigator().SelectSingleNode( _ "/my:myFields/my:EmailAlias", NamespaceManager) emailAlias.SetValue(Me.Application.User.UserName.ToString())
For information about creating XPath expressions, see the XPath Reference on MSDN, and the XML Path Language (XPath) Version 1.0 W3C Recommendation.
Setting the Value of a Node That Has the xsi:nil Attribute
For certain data types, trying to set the value of a blank field programmatically raises the error "Schema validation found non-data type errors." Typically the cause of this error is that the element has the xsi:nil attribute set to true. If you examine the underlying XML element for the blank field in the form, you can see this setting. For example, the XML fragment for the following blank Date field has the xsi:nil attribute set to true.
If the xsi:nil attribute is set to true, it indicates that the element is present but has no value, or in other words, is null. If you try to programmatically set the value of such a node, InfoPath displays the "Schema validation found non-data type errors" message because the element is currently flagged as being null. InfoPath sets the xsi:nil attribute to true for null fields of the following data types:
Whole Number (integer)
Date and Time (dateTime)
To prevent this error, your code must test for the xsi:nil attribute, and if it is present, remove it before setting the value of the node. The following subroutine takes an XpathNavigator object positioned on the node you want to set, checks for the nil attribute, and then deletes it, if it exists.
Public Sub DeleteNil(ByVal node As XPathNavigator) If (node.MoveToAttribute( _ "nil", "http://www.w3.org/2001/XMLSchema-instance")) Then node.DeleteSelf() End If End Sub
You can call this subroutine before you try to set a field of a data type that might have the xsi:nil attribute, as shown in the following example that sets a Date field.
' Access the main data source. Dim myForm As XPathNavigator = MainDataSource.CreateNavigator() ' Select the field. Dim myDate As XPathNavigator = _ myForm.SelectSingleNode("/my:myFields/my:myDate", NamespaceManager) ' Check for and remove the "nil" attribute. DeleteNil(myDate) ' Build the current date in the proper format. (yyyy-mm-dd) Dim curDate As String = DateTime.Today.Year + "-" + _ DateTime.Today.Month + "-" + DateTime.Today.Day ' Set the value of the myDate field. myDate.SetValue(strCurDate)
Although the implementation of the XPathNavigator object in InfoPath 2007 exposes the SetTypedValue method—which is used to set a node using a value of a specific type—InfoPath does not implement that method. You must use the SetValue method instead, and pass a string value of the correct format for the data type of the node.
Selecting and Setting a Set of Repeating Nodes
To specify a set of repeating fields or groups that are of an indeterminate number, use the Select method of the XPathNavigator class. This method returns an XPathNodeIterator object that you can use to iterate over the specified collection of nodes.
The following example assumes that your form template contains a Bulleted List or similar repeating control that is bound to a repeating element named field1. The XPath of the field is passed to the Select method, and the returned XPathNodeIterator is assigned to the nodes variable. You use the MoveNext method to iterate over the collection of nodes, and the Current property to return an XPathNavigator object positioned on the current node. Finally, you use the Value property to retrieve and display the value of each repeating field.
Dim message As String = String.Empty Dim root As XPathNavigator = MainDataSource.CreateNavigator() Dim nodes As XPathNodeIterator = _ root.Select("/my:myFields/my:group1/my:field1", NamespaceManager) Do While nodes.MoveNext message += nodes.Current.Value & System.Environment.NewLine Loop MessageBox.Show(message)
The previous example works with string values in the specified repeating field. However, if the field contains numeric values, you can use similar code to iterate over the values in the field to perform arithmetic, such as calculating the total or average of the values.
Similarly, instead of using the Value property to retrieve the value of each instance of the repeating field, you can use the SetValue method to iterate over the fields and set their values, as shown in the following example.
If you know the range of a set of fields or groups that you want to delete, you can create two XPathNavigator objects positioned on the first and last nodes in the range. To specify each node, use the one-based ordinal position of the nodes in the XPath expression that you pass to the SelectSingleNode method as a predicate enclosed in brackets (). Then use the DeleteRange method of the XPathNavigator class to delete the range of nodes.
The following example deletes the nodes for the second, third, and fourth fields in the specified group.
Dim root As XPathNavigator = MainDataSource.CreateNavigator() Dim first As XPathNavigator = _ root.SelectSingleNode("/my:myFields/my:group1/my:field1", _ NamespaceManager) Dim last As XPathNavigator = _ root.SelectSingleNode("/my:myFields/my:group1/my:field1", _ NamespaceManager) first.DeleteRange(last)
If you want to delete all the fields in a group, but you do not know the ordinal value of the node for the last field, you can use the XPath position function and the last function in the predicate for selecting the last node to pass to the DeleteRange method, as shown in the following example.
Dim myGroup As String = "/my:myFields/my:group1/my:field1" Dim root As XPathNavigator = MainDataSource.CreateNavigator() Dim first As XPathNavigator = root.SelectSingleNode( _ myGroup + "", NamespaceManager) Dim last As XPathNavigator = root.SelectSingleNode(myGroup + _ "[position()=last()]", NamespaceManager) first.DeleteRange(last)
When a user adds or changes data in the form's underlying XML document, InfoPath raises one of the events defined by the XmlEvent class. For example, the Changed() event for a field occurs after a change to the field has been validated and accepted. You typically use this event to change some other data in the form, or to perform calculations after the change to the current field or group has occurred. When the Changed event is raised, InfoPath passes an XmlEventArgs object to the handler for the event that provides properties for working with the node you want to change.
The next example uses the Site() property of the XmlEventArgs object, which returns an XPathNavigator object positioned at the node that raised the event. The example assumes a form template with a Repeating Table control. The control contains two columns that are bound to the SelectCustomer field and the CustomerName field of the form's main data source. The SelectCustomer field is bound to a Drop-Down List Box control that looks up the CustomerID value from a Customers element in an external data source named "Customers."
In the example, the data source is an XML file named "Customers.xml" that you add to the form template by using the Resource Files command on the Tools menu. You then specify it as a data source by using the Data Connections command. However, you can also retrieve the data from another kind of secondary data source, such as a SharePoint list or SQL Server database. The Customers.xml file is structured as shown in the following XML fragment.
<?xml version="1.0" encoding="UTF-8"?> <Customers> <Customer> <CustomerID>ALFKI</CustomerID> <CompanyName>Alfreds Futterkiste</CompanyName> <ContactName>Maria Anderss</ContactName> <ContactTitle>Sales Representative</ContactTitle> <Address>Obere Str. 57</Address> <City>Berlin</City> <PostalCode>12209</PostalCode> <Country>Germany</Country> <Phone>030-0074321</Phone> <Fax>030-0076545</Fax> </Customer> <Customer> <CustomerID>ANATR</CustomerID> <CompanyName>Ana Trujillo Emparedados y helados</CompanyName> <ContactName>Ana Trujillo</ContactName> <ContactTitle>Owner</ContactTitle> <Address>Avda. de la Constitución 2222</Address> <City>México D.F.</City> <PostalCode>05021</PostalCode> <Country>Mexico</Country> <Phone>(5) 555-4729</Phone> <Fax>(5) 555-3745</Fax> </Customer> ... </Customers>
In the Properties dialog box of the Drop-Down List Box control, you specify that the list box entries are looked up from the CustomerID element of the Customers data source. To add the event handler to the SelectCustomer field for the Changed() event, right-click the control that is bound to the field, point to Programming, and then click Changed Event.
Figure 1 shows the Drop-Down List Box control that is bound to the SelectCustomer field. It displays data from the CustomerID element of the Customers.xml secondary data source.
The code in the following example shows the event handler for the Changed() event of the SelectCustomer field. The event handler performs the following operations when a user selects a value from the Drop-Down List Box bound to the field:
Creates an XPathNavigator positioned at the root of the "Customers" secondary data source.
Creates an XPathNavigator positioned on the SelectCustomer field in the main data source by using the Site property.
Uses the MoveToParent method of the XPathNavigator class to enable access to its sibling, the CustomerName field, in the current row of the Repeating Table control.
Checks whether the user selected the blank value at the top of the Drop-Down List Box after previously selecting a CustomerID value, and if so, clears the value from the CustomerName field.
Gets the company name from the CompanyName element contained within a Customer element in Customers.xml file using an XPath expression that matches the CustomerID field that is selected in the Drop-Down List Box control.
Selects the CustomerName field from the parent node (the current row in the Repeating Table control) and sets its value to the value that is retrieved from the matching CompanyName element.
Public Sub SelectCustomer_Changed( ByVal sender As Object, ByVal e As XmlEventArgs) Try ' Get a reference to the Customers secondary data source, ' and create an XPathNavigator object for it. Dim Customers As XPathNavigator = _ Me.DataSources("Customers").CreateNavigator ' Create an XPathNavigator object positioned on the current site, ' which is the SelectCustomer node in the main data source. Dim myForm As XPathNavigator = e.Site.CreateNavigator ' Move to the parent of the SelectCustomer node, so that the code ' can access the CustomerName node. myForm.MoveToParent() ' Check if the user selected the blank value from the ' Drop-Down List Box after previously selecting a CustomerID. If e.Site.InnerXml = "" Then ' Clear previously selected value from CustomerName field. myForm.SelectSingleNode("my:CustomerName", _ Me.NamespaceManager).DeleteSelf() Else ' Get the Company name from the Customers data source using ' the value in the CustomerID node. Dim custName As String = Customers.SelectSingleNode( _ "/Customers/Customer[CustomerID = '" & e.Site.InnerXml & _ "']/CompanyName", Me.NamespaceManager).InnerXml ' Select the CustomerName node for the current row ' and set its value. myForm.SelectSingleNode("my:CustomerName", _ Me.NamespaceManager).SetValue(custName) End If Catch ex As Exception MessageBox.Show("Error: " & ex.Message) End Try End Sub
In addition to working directly with the data sources of the form, as shown in the previous examples, the View class provides methods for working with data based on the controls in the current view. For example, you can work with nodes based on a user's current selection within a control, or across one or more controls. Most of these methods also accept or return XPathNavigator or XPathNodeIterator objects.
You cannot use the GetContextNodes and GetSelectedNodes methods from a Button control on the form because when the user clicks the Button, the focus is moved away from the control or controls that contain the data you want to work with. But you can write functions using these methods that you can call from a custom task pane, menu command, or toolbar.
Table 1 provides a list of View class methods that you can use to work with data that is bound to controls on the form.
Executes an editing command against a form's underlying XML document based on the current selection or view context.
Gets a reference to an XPathNodeIterator object for iterating over nodes based on the current context.
Gets a reference to an XPathNodeIterator object for iterating over all XML nodes in the current selection of items in a view.
Selects a range of nodes in a view.
Selects the text contained in an editable control that is bound to the specified node.
Many of the classes and members of the InfoPath managed code object model provided by the Microsoft.Office.InfoPath namespace use the XPathNavigator and XPathNodeIterator classes and their members to work with a form template's data sources. This article has provided an introduction to how to work with some members of these classes to access data in a form's underlying XML document (its main data source) and secondary data sources.
For more information about working with the XPathNavigator and XPathNodeIterator classes, see the topics in the Process XML Data Using the XPath Data Model section of the .NET Framework Developer's Guide. For more information about working with the classes and members of the Microsoft.Office.InfoPath namespace, see the InfoPath Developer Reference for Managed Code.
Information about the XML standards and technologies that are used by InfoPath.
Information about InfoPath developer resources.
Office Online site that provides information about InfoPath form design, declarative logic, and other features that are available through the InfoPath user interface.
The following table provides a summary of all the members in the Microsoft.Office.InfoPath namespace that use the XPathNavigator class to access, manipulate, or submit XML data.
GetNamedNodeProperty(XPathNavigator, String) method
SetNamedNodeProperty(XPathNavigator, String, String) method
Add(XPathNavigator, String, String) methods
Execute(XPathNavigator, XPathNavigator, XPathNavigator) method
MainDataSource() property, which returns a DataSource object that in turn provides the CreateNavigator() method for creating an XPathNavigator object positioned at the root of the form's underlying XML document (main data source).
NewFromFormTemplate(String, XPathNavigator) method
ReportError(XPathNavigator, Boolean, String) methods
In addition to the InfoPath object model members that return or accept an XPathNavigator object, the following methods return an instance of the XPathNodeIterator class of the System.Xml.XPath namespace for iterating over the XML nodes of items that are specified or selected in a view.