Chapter 10
Managing XML

VFP 9 again improves the usability of XML in VFP applications by enhancing the XMLAdapter classes as well as XML functions. The enhancements center on nested XML, multiple language, and better XPATH support. VFP 9 applications can now produce and consume a wider range of XML documents.

Extensible Markup Language (XML) is slowly but surely becoming a standard for data transmission and is supported in most Microsoft applications. VFP has had XML capabilities since version 7 when the XMLTOCURSOR(), XMLUPDATEGRAM(), and CURSORTOXML() functions were added. VFP 8 introduced the XMLAdapter classes (including XMLTable and XMLField) to overcome the limitations of the existing functions. The XMLAdapter classes in VFP 8 work fairly well, but still have some limitations. For one, the XMLAdapter is fairly picky about the schemas it can understand. Also, there is no easy way to create nested or hierarchical XML. The only way to do so is manually creating the document using something like text merge. VFP 9 lifts many of the XML limitations imposed in VFP 8 and earlier.

Creating nested XML

One of the most useful enhancements in VFP 9 is the improved ability for the XMLAdapter to read and write hierarchical XML. Under VFP 8, the XMLAdapter can not create nested XML using the ToXML method nor can it always parse nested data into multiple tables using ToCursor or ChangesToCursor. VFP 9 lifts this limitation.

The most straightforward way to get VFP data into the XMLAdapter is to use the AddTableToSchema method. This method loads the data from an open VFP cursor into the adapter. In VFP 8, you could load several tables into the adapter, but each table appeared at the same level in the XML document. For example, the code in Listing 1 produces the results in Figure 1.

Text Box: "

The program Old_AddTableToSchema.PRG is included with the Developer Download files, available at www.hentzenwerke.com, for this chapter.

Listing 1. Creating an XML document from more than one table without nesting.

LOCAL ;

     loXMLAdapter AS XMLAdapter

 

CLOSE DATABASES ALL

OPEN DATABASE (HOME() + [samples\northwind\northwind])

USE Categories IN 0

USE Products IN 0

 

loXMLAdapter = CREATEOBJECT([XMLAdapter])

 

WITH loXMLAdapter

     .XMLSchemaLocation = [Old_AddTableToSchema.xsd]

     .AddTableSchema([Categories])

     .AddTableSchema([Products])

     .ToXML([Old_AddTableToSchema.xml], [], .T.)

     MODIFY FILE Old_AddTableToSchema.xml

ENDWITH

 

loXMLAdapter = .NULL.

 

Figure 1. The Old_AddTableToSchema.XML document is displayed in Internet Explorer for easier viewing. It shows multiple tables in a single XML document. Notice there is no nesting.

The nested XML in Figure 2 is easily generated by the XMLAdapter using the new properties and methods added in VFP 9.

Figure 2. Multiple tables in a single XML document with nesting.

RespectNesting

A new property RespectNesting was added to the XMLAdapter to control the default nesting behavior. This property controls the nesting behavior as tables are added to the schema as well as how the XML is generated. In order to nest tables in the XMLAdapter, a relation must exist using SET RELATION.

If the RespectNesting property is .T. when tables are added to the schema, they are added in a nested format. If the property is still true when the XML document is created using ToXML, the resulting document contains nested XML. Therefore, the code in Listing 2 produces the XML document displayed in Figure 2.

Text Box: "

The program AddTableToSchema.PRG is included with the Developer Download files, available at www.hentzenwerke.com, for this chapter.


Listing 2. Creating a nested XML document.

LOCAL ;

     loXMLAdapter

 

CLOSE DATABASES ALL

OPEN DATABASE (HOME() + [samples\northwind\northwind])

USE Categories IN 0

USE Products IN 0 ORDER CategoryID

SELECT Categories

SET RELATION TO CategoryId INTO Products ADDITIVE

 

loXMLAdapter = CREATEOBJECT([XMLAdapter])

 

WITH loXMLAdapter AS XMLAdapter

     .XMLSchemaLocation = [AddTableToSchema_Nest.xsd]

     .RespectNesting = .T.

     .AddTableSchema([Categories])

     .AddTableSchema([Products])

     .ToXML([AddTableToSchema_Nest.xml], [], .T.)

     MODIFY FILE AddTableToSchema_Nest.xml

ENDWITH

 

loXMLAdapter = .NULL.

If RespectNesting is set to .F. prior to calling ToXML, the XML document is created without nesting the table data. This is the same behavior seen in Visual FoxPro 8. In addition, if RespectNesting is .F. when the tables are added, the tables are not added in a nested structure unless .T. is passed to AddTableToSchema as the last parameter.

lAutoNest

An eighth argument, lAutoNest, was added to the AddTableToSchema method to support hierarchical XML. Like RespectNesting, this argument controls which tables are nested as the schema is added to the XMLAdapter. If this argument is .T., the table is nested according to the relations involving the table. If .F. is passed, the table is not nested. If no value is passed, the RespectNesting property of XMLAdapter controls nesting. In all cases, a relation must exist between the two tables for nesting to occur. The XML shown in Figure 2 can also be generated using the code in Listing 3.

Text Box: "

The program AnotherAddTableToSchema.PRG is included with the Developer Download files, available at www.hentzenwerke.com, for
this chapter.

Listing 3. Another way to create a nested XML document.

LOCAL ;

     loXMLAdapter

 

CLOSE DATABASES ALL

OPEN DATABASE (HOME() + [samples\northwind\northwind])

USE Categories IN 0

USE Products IN 0 ORDER CategoryID

SELECT Categories

SET RELATION TO CategoryId INTO Products ADDITIVE

 

loXMLAdapter = CREATEOBJECT([XMLAdapter])

 

WITH loXMLAdapter AS XMLAdapter

     .XMLSchemaLocation = []

     .RespectNesting = .F.

     .AddTableSchema([Categories])

     .AddTableSchema([Products],,,,,,,.T.)

     .RespectNesting = .T.

     .ToXML([AnotherAddTableToSchema_Nest.xml], [], .T.)

     MODIFY FILE AnotherAddTableToSchema_Nest.xml

ENDWITH

 

loXMLAdapter = .NULL.

While it seems the easiest way to create hierarchical XML is to set the RespectNesting property of the XMLAdapter class before adding any tables, it is an all or nothing solution. The lAutoNest parameter gives you some flexibility in determining which tables are nested and which ones are not.

New errors

VFP 9 has two new error numbers to handle situations where hierarchical XML cannot be generated. Error 2178, “There is no relation to the nested table <table name>,” occurs if you call ToXML when the tables in the XMLAdapter are nested, the RespectNesting property is .T., and no relation exists between the data.

The second error, 2179 (“Nested XML format is incompatible with 'Changes Only' mode”), occurs when the tables are nested, RespectNesting is .T., and the lChangesOnly parameter passed to the ToXML method has a value of .T. The XMLAdapter class does not support nested changes.

XMLTable enhancements

The XMLTable class was also enhanced to support hierarchical XML. It has three new read-only properties, shown in Table 1, to indicate how tables are nested.

Table 1. New XMLTable properties.

Property

Description

NestedInto

An object reference to the parent of a table if the table is nested. If the table is not nested, .NULL. is returned.

NextSiblingTable

An object reference to the next sibling table if the table is nested and there is a sibling. If the table is not nested or there is no sibling, .NULL. is returned.

FirstNestedTable

An object reference to the first nested table if there are nested tables. If there is no nested table, .NULL. is returned.

 

Along with the three new properties, two new methods were added for more flexibility. The Nest method manually nests one table object inside another. The syntax for this method is:

XMLTable.Nest( oXMLTable [ , oAfterXMLTable | nAfterIndex ])

XMLTable is a reference to the outer table and oXMLTable is an object reference to
the table that should be nested. The second parameter is used to control the location where oXMLTable is nested. oAfterXMLTable is an object reference to the table that should
come before oXMLTable in the hierarchy. nAfterIndex is used to determine location by number rather than object reference. If an index or table object is not specified, the new
table is inserted as the last table in the nesting order. Table 2 shows the possible values
for nAfterIndex.

When using the Nest method, tables do not need to be related. However, in order to create hierarchical XML based on the nested tables the relations must be set before calling the ToXML method.

Table 2. Possible values for the nAfterIndex parameter on the Nest method.

Value

Meaning

0

Nest the table first in the nesting order.

-1

Nest the table last in the nesting order.

Valid index position

Nest the table after the table at the specified position.

Invalid index position

Nest the table last in the nesting order.

 

When nesting tables, the tables must be associated with the same XMLAdapter and they cannot be nested recursively. A new error, 2177, “Inner and outer XMLTable objects are not associated with the same XMLAdapter object,” occurs if you try to nest tables from two different XMLAdapter objects. The error, 2187, “Recursive XMLTable nesting is not allowed,” occurs if you try to nest XMLTable objects into themselves or nested child XMLTable objects.

The second new method, UnNest, removes an XMLTable from being nested. The syntax is as follows:

XMLTable.UnNest()

The two examples in Listing 4 and Listing 5 show how to use the properties and methods of the XMLTable class. The first example, ManualNest, shows how to use the Nest method to manually nest tables after the schema is added to the adapter. The second example, ShowNextSibling, shows how to use the NextSibling property to step through all of the children of a parent table. The FirstNestedTable property provides an object reference to the first nested table and the NextSibling property is used to reference all other nested tables.

Text Box: "

The programs ManualNest.PRG and ShowNextSibling.PRG are included
with the Developer Download files, available at www.hentzenwerke.com, for this chapter.


Listing 4. Manually nesting tables in the XMLAdapter.

* ManualNest.PRG

LOCAL ;

     loXMLAdapter AS XMLAdapter, ;

     loOrders AS XMLTable, ;

     loDetails AS XMLTable, ;

     loProducts AS XMLTable, ;

     loEmployees AS XMLTable

 

CLOSE DATABASES ALL

OPEN DATABASE (HOME() + [samples\northwind\northwind])

USE Orders IN 0

USE OrderDetails IN 0 ORDER OrderId

USE Products IN 0 ORDER ProductId

USE Employees IN 0 ORDER EmployeeId

SELECT Orders

SET RELATION TO OrderID INTO OrderDetails ADDITIVE

SET RELATION TO EmployeeId INTO Employees ADDITIVE

SELECT OrderDetails

SET RELATION TO ProductId INTO Products ADDITIVE

 

loXMLAdapter = CREATEOBJECT([XMLAdapter])

 

WITH loXMLAdapter AS XMLAdapter

     .RespectNesting = .F.

     .XMLSchemaLocation = []

     .AddTableSchema([Orders])

     .AddTableSchema([OrderDetails])

     .AddTableSchema([Products])

     .AddTableSchema([Employees])

     loOrders = .Tables(1)

     loDetails = .Tables(2)

     loProducts = .Tables(3)

     loEmployees = .Tables(4)

 

     loOrders.Nest(loDetails)

     loOrders.Nest(loEmployees)

     loDetails.Nest(loProducts)  

     ? [The first relation from Orders is: ] + loOrders.FirstNestedTable.Alias

     ? [The Employee table is nested into:] + loEmployees.NestedInto.Alias

ENDWITH

 

loXMLAdapter = .NULL.

Listing 5. Using the FirstNestedTable and NextSiblingTable properties to step through the hierarchy.

*ShowNextSibling.PRG

LOCAL ;

     loXMLAdapter AS XMLAdapter, ;

     loOrders as XMLTable, ;     

     loNextTable AS XMLTable, ;

     lnI as Integer

 

CLOSE DATABASES ALL

OPEN DATABASE (HOME() + [samples\northwind\northwind])

USE Orders IN 0

USE Customers IN 0 ORDER CustomerId

USE OrderDetails IN 0 ORDER OrderId

USE Shippers IN 0 ORDER ShipperId

USE Employees IN 0 ORDER EmployeeId

SELECT Orders

SET RELATION TO CustomerId INTO Customers ADDITIVE

SET RELATION TO OrderID INTO OrderDetails ADDITIVE

SET RELATION TO ShipVia INTO Shippers ADDITIVE

SET RELATION TO EmployeeId INTO Employees ADDITIVE

                         

loXMLAdapter = CREATEOBJECT("XMLAdapter")

 

WITH loXMLAdapter AS XMLAdapter

     .RespectNesting = .T.

     .AddTableSchema([Orders])

     .AddTableSchema([Customers])

     .AddTableSchema([OrderDetails])

     .AddTableSchema([Shippers])

     .AddTableSchema([Employees])

     loOrders = .Tables(1)

     lnI = 1

     loNextTable = loOrders.FirstNestedTable

 

     DO WHILE NOT ISNULL(loNextTable)

          ? [The cursor: ] + loNextTable.Alias + ;

                [ is nested in Orders at Level ] + TRANSFORM(lnI)

          lnI = lnI + 1

          loNextTable = loNextTable.NextSiblingTable

     ENDDO

 

ENDWITH

 

loXMLAdapter = .NULL.

Consuming nested XML

So far, the examples show how to create nested XML. However, the new features of XMLAdapter apply when reading XML documents as well. The LoadXML method now populates the new nesting properties as the document is loaded. Take for example the XML
in Listing 6.

Listing 6. Nested XML.

<SalesOrder>

   <Order>

      <OrderId>6715</OrderId>

      <Description>An Order for some things.</Description>

      <OrderDetail>

         <OrderDetailId>5240</OrderDetailId>

         <OrderId>6715</OrderId>

         <Category>2</Category>

         <Remark>4/10/2003 12:05:14 PM</Remark>

      </OrderDetail>

      <OrderDetail>

        <OrderDetailId>5241</OrderDetailId>

         <OrderId>6715</OrderId>

         <Category>4</Category>

         <Remark>4/10/2003 12:05:14 PM</Remark>

      </OrderDetail>

      <OrderDetail>

         <OrderDetailId>5242</OrderDetailId>

         <OrderId>6715</OrderId>

         <Category>1</Category>

         <Remark>4/10/2003 12:05:14 PM</Remark>

      </OrderDetail>

   </Order>

</SalesOrder>

To save space the schema was removed from this document. However, with a valid schema, the following program loads both the Order and OrderDetails tables into the Tables collection of the XMLAdapter and sets the nesting properties correctly. In VFP 8 the tables collection is populated correctly, but there is no data to indicate that the tables are nested. The code in Listing 7 displays alias names for both tables (Order and OrderDetail) and shows that OrderDetail is nested into Orders.

Text Box: "

The program ConsumeNested.PRG is included with the Developer Download files, available at www.hentzenwerke.com, for this chapter.

Listing 7. With VFP 9, the XML is parsed and the table collection is populated, but the nesting properties are also set so the hierarchy can be read from the VFP side.

LOCAL ;

     loAdapter AS XMLAdapter

 

loAdapter = CREATEOBJECT([XMLAdapter])

WITH loAdapter AS XMLAdapter

     .RespectNesting = .T.

 

     IF .LoadXML([orders.xml], .T.)

          ? [The number of tables read is: ] + TRANSFORM(.Tables.Count)

          ? [The first table is: ] + .Tables(1).Alias

          ? [The second table is: ] + .Tables(2).Alias

          ? [The first table is nested into ] + .Tables(1).NestedInto.Alias

     ENDIF

 

ENDWITH

 

loAdapter = .NULL.

XPath expressions for XML documents

XPath is a language used, mainly by Extensible Stylesheet Language (XSLT), for extracting portions of an XML document. The language is similar to the old DOS syntax used to navigate directory structures. For example the syntax ..\ refers to an XML node one above the currently selected node. The XMLAdapter class in VFP 9 has new XPath features to help manage large, complex XML documents. Some very good, basic XPath tutorial information is available at http://www.zvon.org/xxl/XPathTutorial/General/examples.html if you want more information on XPath.

XMLNameIsXPath

The XPath features added to the XMLAdapter take the form of three properties. The most notable property is the XMLNameIsXPath property. This property allows you to specify an XPath reference as the name for an XMLAdapter element because the XMLNameIsXPath property applies to XMLAdapter, XMLTable, and XMLField. The following example, Listing 8, uses an XML document called CategoriesAndProducts.XML.

Listing 8. Creating a “virtual” field using the XMLAdapter.

* CarryOverData.prg

LOCAL ;

    loXMLAdapter, ;

    loXMLTable, ;

    loXMLField

 

CLOSE DATABASES all

CLEAR

 

loXMLAdapter = CREATEOBJECT([XMLAdapter])

loXMLAdapter.LoadXML([CategoriesAndProducts.XML],.T.)

 

* This code createa a new XMLField object with an XPath reference

* as the name. It will cause a "virtual" field containing the

* category name to be added to the products table when it is

* rendered to a cursor.

loXMLField = CREATEOBJECT([XMLField])

loXMLField.XMLNameIsXPath = .T.

loXMLField.XMLName = STRCONV([parent::Categories/categoryname], 5)        

loXMLField.Alias = [CategoryName]

loXMLField.DataType = [C]

loXMLField.MaxLength = 15

loXMLTable = loXMLAdapter.Tables(STRCONV([Products],5))

loXMLTable.Fields.Add(loXMLField, loXMLField.XMLName)

 

FOR EACH loXMLTable IN loXMLAdapter.Tables

    loXMLTable.ToCursor()

NEXT

 

SELECT Products

BROWSE

Figure 3 displays the results produced by the program code in Listing 8. Notice the Category name in the BROWSE window. This data comes from the nested table in the
XML document.

Figure 3. The program in Listing 8. Creating a “virtual” field using the XMLAdapter.

The program code in Listing 9 shows how to use XPath to filter out records that don’t match a certain condition. The results are displayed in Figure 4.

Listing 9. Use XPath to filter out only products with a supplierid >10.

* FilterXML.prg

LOCAL ;

    loXMLAdapter, ;

    loXMLTable

 

CLOSE DATABASES all

CLEAR

 

loXMLAdapter = CREATEOBJECT([XMLAdapter])

loXMLAdapter.LoadXML([CategoriesAndProducts.XML], .T.)

 

* Filter so only products with a supplier id > 10 appear

 

* Get a reference to the products table.

loXMLTable = loXMLAdapter.Tables(STRCONV([Products],5))

 

* Remove the products data.

loXMLAdapter.Tables.Remove(STRCONV([Products],5))

loXMLTable.XMLNameIsXPath = .T.

 

* Set the XMLName property of the old products table to an XPath expression.

loXMLTable.XMLName = STRCONV([Categories/Products[supplierid>=10]], 5)

 

* Add the table back to the adapter.

loXMLAdapter.Tables.Add(loXMLTable, loXMLTable.XMLName)

 

FOR EACH loXMLTable IN loXMLAdapter.Tables

    loXMLTable.ToCursor()

NEXT

 

SELECT Products

BROWSE

Figure 4. The results after running the code in Listing 9.

Text Box: ¥

Be very careful when specifying the XPath reference because XML is
case sensitive. You must reference tags in the same case as they appear
in the document.

Text Box: "

The files CategoriesAndProducts.XML, CarryOverData.PRG, and FilterXML.PRG are included with the Developer Download files, available at www.hentzenwerke.com, for this chapter.

SelectionNamespaces

This property allows you to specify a custom namespace (prefix) for XPath expressions when loading XML documents. This property only applies to the XMLAdapter class and corresponds to the SelectionNamespaces property of the XML DOM. For more information see the MSDN documentation on MSXML4 at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/html/xmpro2nd_selectionnamespaces.asp

DeclareXMLPrefix

This new logical property of the XMLAdapter and XMLTable determines if the XMLAdapter should declare an XML Prefix when parsing XML. If the XMLPrefix or XMLNamespace properties are empty, this property is ignored as there is no prefix/namespace to declare. If the RespectNesting property of an XMLAdapter object is true, this property applies to the XMLAdapter and all children (XMLTables) in the document. This is similar to the SelectionNamespaces property. However, SelectionNamespaces applies to all parsing operations, not just the current nesting scheme.

More new errors

The new XPATH support in the XMLAdapter is mainly for reading complex XML documents into VFP cursors. For this reason, several new errors numbers were added. Table 3 shows the new error numbers and what they mean.


Table 3. New error numbers related to XPATH support in XMLAdapter.

Error Number

Description

2180

This error applies when XMLAdapter.XMLNameIsXPath is true. The property is not supported when creating XML, so the error is raised when the ToXML Method is called and the XMLName is not empty. The error also occurs when calling ApplyDiffGram, ChangesToCursor, or ToCursor and IsDiffGram is also .T.

2181

This error applies when XMLTable.XMLNameIsXPath is true and ToXML, ApplyDiffGram, or ChangesToCursor are called. This is different from error 2180, which applies to the XMLAdapter and this error applies to XMLTable.

2182

This error occurs when XMLNameIsXPath is true and the XMLName property contains a value that is not a legal XPath reference.

Non-Dataset XSD schema support

In VFP 8, when an XML document with an XSD schema is loaded into an XMLAdapter, the tables collection is only populated if the schema is in the ADO.NET DataSet format. If not, the XML is loaded, but the tables collection is not populated.

In VFP 9, the XMLAdapter class now supports XSD schemas in other formats. The schema shown in Listing 10 is a standard, but non-ADO.NET DataSet, XSD schema. This schema was derived from an example schema found at www.xml.com.

Text Box: "

The files Library.XSD, Books.XML, and NoDatasetXSD.PRG are included with the Developer Download files, available at www.hentzenwerke.com, for this chapter.

Listing 10. Library.XSD is a standard XSD schema not in the ADO.NET
DataSet format.

<?xml version="1.0" encoding="utf-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

 <xs:element name="book">

  <xs:complexType>

   <xs:sequence>

    <xs:element name="title" type="xs:string"/>

    <xs:element name="author" type="xs:string"/>

    <xs:element name="character" minOccurs="0" maxOccurs="unbounded">

     <xs:complexType>

      <xs:sequence>

       <xs:element name="name" type="xs:string"/>

       <xs:element name="friend-of" type="xs:string" minOccurs="0"


maxOccurs="unbounded"/>

       <xs:element name="since" type="xs:date"/>

       <xs:element name="qualification" type="xs:string"/>

      </xs:sequence>

     </xs:complexType>

    </xs:element>

   </xs:sequence>

   <xs:attribute name="isbn" type="xs:string"/>

  </xs:complexType>

 </xs:element>

</xs:schema>

The XML document, Books.XML, that references this schema is shown in Listing 11. This XML document was also derived from an example found at www.xml.com.  

Listing 11. Books.XML references the schema in Listing 10

<?xml version="1.0" encoding="utf-8" ?>

<book isbn="0836217462" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="library.xsd">

 <title>Being a Dog Is a Full-Time Job</title>

 <author>Charles M. Schulz</author>

 <character>

   <name>Snoopy</name>

   <friend-of>Peppermint Patty</friend-of>

   <since>1950-10-04</since>

   <qualification>extroverted beagle</qualification>

 </character>

 <character>

   <name>Peppermint Patty</name>

   <since>1966-08-22</since>

   <qualification>bold, brash and tomboyish</qualification>

 </character>

</book>

If you run the following program in both VFP 8 and 9, you will see the difference in the number of tables in the collection. In VFP 8 the number is zero and in VFP 9 it is two.

Listing 12. NoDatasetXSD.PRG loads an XML document that uses a non-ADO.NET DataSet schema.

LOCAL ;

     loAdapter AS XMLAdapter

 

loAdapter = CREATEOBJECT([XMLAdapter])

 

WITH loAdapter

 

     IF .LoadXML([books.XML], .T.)

          ? [The number of tables in the tables collection is: ] + ;

               TRANSFORM(.Tables.Count)

     ENDIF

 

ENDWITH

In addition, if the RespectNesting property of the XMLAdapter is .T. when the XML is loaded, the tables are nested according to the XML schema.

When XML with a non-Dataset schema is loaded, the XMLName and XMLPrefix properties are left empty. These properties are populated when using a Dataset based schema.

Multi-language support

XMLAdapter is enhanced for VFP 9 to provide better language support when generating and parsing XML documents.

Three methods that read XML data into VFP cursors, ToCursor, ApplyDiffGram, and ChangesToCursor, are enhanced to include a new nCodePage parameter. This parameter represents the code page number to use when the VFP cursor is created. This parameter, if passed a non-zero value, always controls the code page of the newly created cursor.

The XMLAdapter, XMLTable, and XMLField classes are also enhanced to include a CodePage property. This property controls how the cursor is created and/or how field data is populated. Set this property to a valid code page number or an error occurs.

A new property, UseCodePage, was also added to the XMLAdapter class. This property controls the encoding of data when XML is generated as well as the creation of cursors if the code page parameter is not passed.

Let’s start with how these features work when a cursor is created. If the code page parameter is not passed to the ToCursor, ApplyDiffGram, or ChangesToCursor methods, the CodePage property of the XMLTable class dictates the code page for the cursor. If the XMLTable.CodePage property is zero, the XMLAdapter.CodePage is used. All of this happens only when the UseCodePage property is true. If UseCodePage is false, the cursor is created just as it was in VFP 8 using the default code page.

Once a cursor is created and field data is being written, the XMLField object’s CodePage determines the code page translation for the actual data. If the CodePage property of the field object is zero, the code page of the cursor applies.

Things are a little different when creating XML. The UseCodePage property controls the generation of the XML and works in conjunction with the RespectCursorCP and UTF8Encoded properties. Table 4 shows the difference in behaviors.

Table 4. Behaviors associated with UseCodePage, RespectCursorCP, and UTF8Encoded.

UseCodePage

RespectCursorCP

UTF8Encoded

Behavior

.T.

.T.

.T.

UTF-8 is used as the encoding for the XML document.

Character data is translated using the cursor’s code page or XMLField.CodePage if it is greater than zero.

Unicode data is translated using UTF-8.

.T.

.T.

.F.

Cursor’s code page is used for the XML document encoding.

Character data is not translated.

Unicode data is translated using the cursor’s code page.

If an XMLField CodePage is greater than zero, but does not match the cursor’s code page, an error occurs.

.T.

.F.

.T.

UTF-8 is used as the encoding for the XML document.

Character data is translated using the current code page.

Fields with NOCPTRANS are not translated.

Unicode data is translated using UTF-8.

.T.

.F.

.F.

Windows-1252 is used for XML document (root node)

Character data is translated using the current code page.

Fields with NOCPTRANS are not translated.

Unicode data is translated using code page 1252.

.F.

.T.

.T.

UTF-8 is used as the encoding for the XML document.

Character data is translated using the current code page.

Fields marked as NOCPTRANS are translated to UTF-8 using the code page specified by SYS(3005).

Unicode data is translated using UTF-8.

.F.

.T.

.F.

Cursor’s code page is used for the XML document encoding.

Character data is not translated.

Unicode data is translated using the cursor’s code page.

.F.

.F.

.T.

UTF-8 is used as the encoding for the XML document.

Character data is translated using the current code page.

Fields with NOCPTRANS are not translated.

Unicode data is translated using UTF-8.

.F.

.F.

.F.

Windows-1252 is used for XML document

Character data is translated using the current code page.

Fields with NOCPTRANS are not translated.

Unicode data is translated using code page 1252.

 

Text Box: ¥

See the Help Topic “Code Pages Supported by Visual FoxPro” in the VFP Help File for a list of valid code page numbers.

In addition to multi-language support in the XMLAdapter class, this support is also available with the following functions using the nFlag parameter.

·         XMLTOCURSOR()

·         CURSORTOXML()

·         XMLUPDATEGRAM()

A new flag value of 32768 indicates that a code page should be used. This is similar to setting the UseCodePage property of the XMLAdapter class to .T. and respects the flag values for RespectCursorCP (16) and UTF8Encoded (32) as seen in Table 4. If VFP is unable to determine the code page when using the 32768 flag, the error message “Unable to infer cursor’s code page from XML document” displays. The error number for this error is 2185.

Data type support

XMLAdapter has two new properties to support the mapping of remote data to the new data types in VFP 9. Table 5 lists these properties. Please see Chapter 9, “New Data and Index Types,” for more information on the new data types and possible values for these properties.


Table 5. New XMLAdapter data type conversion properties.

Property

Description

MapBinary

Specifies how binary data from the remote data source is mapped to VFP data types. Setting the property to .T. means the VarBinary data returned from a remote database is mapped to the new VarBinary data type unless the value is longer than 254 in which case it is mapped to the new Blob data type. Setting the property to .F. (the default) mimics the existing FoxPro behavior and maps VarBinary data values to Character data types unless the value is longer than 254. In this case, the data is mapped to a memo or general field.

MapVarChar

Specifies how Varchar data from the remote data source is mapped to VFP data types. Setting the value to .T. results in VarChar values returned from a remote database stored as VarChar on the FoxPro side. Setting the value to .F. results in VarChar data mapping to character data types. The default value is .F.

 

In addition to XMLAdapter support for the new data types, an existing function, TTOC(), is enhanced to better support XML data. TTOC() has the following syntax:

TTOC(tExpression [, 1 | 2 | 3])

VFP 9 adds a new value of 3 for the second argument to this function. This value tells TTOC() to return the character string in a valid XML data format. The format is yyyy-mm-ddThh:mm:ss. The return value is always 19 characters regardless of SET CENTURY and SET SECONDS.

For example:

ltTime = DATETIME()

? TTOC(ltTime, 3)     && Returns 2004-06-25T14:35:46

Summary

As the XML standards continue to evolve, we see each version of VFP step up support for the current standard. Since XML support was added in VFP 7, the number and type of documents that cannot be handled with a VFP class or function have become fewer and fewer. Version 9 further narrows the gap.

 

 

Updates and corrections for this chapter can be found on Hentzenwerke’s website, www.hentzenwerke.com. Click “Catalog” and navigate to the page for this book.