http://www.developer.com/

Back to article

Working with XML in Managed C++


August 23, 2002

From Kate Gregory's Codeguru column, "Using Visual C++ .NET".

In my last column, I showed you how to use MSXML4, the COM component that parses, searches, and generates XML, to work with XML in a "classic Visual C++" application. This time around I'm going to tackle the same tasks, but use the .NET Framework to process XML.

The advantages of using the XML functionality that are built into the .NET class libraries include:

  • Nothing to install, deploy, or redistribute - your apps need the framework anyway, and once you have the framework you have everything you need

  • Simpler code because you're not working with COM - for example you don't need to call CoInitialize() and CoUninitialize().

  • Extra functionality that wasn't in MSXML4.

Sample XML

I'm going to use the same sample XML as I did in the previous column. Here's how it looks:

<?xml version="1.0" encoding="utf-8" ?> 
<PurchaseOrder>
<Customer id="123"/>
<Item SKU="1234" Price="4.56" Quantity="1"/>
<Item SKU="1235" Price="4.58" Quantity="2"/>
</PurchaseOrder>

Loading XML with XmlDocument

The classes for working with XML are in the System::Xml namespace. The XmlDocument represents a DOM document, a tree into which your XML can be loaded. It's the same idea as the DOMDocument you worked with using MSXML4. Here's a simple Managed C++ application that loads a file of XML into memory:

#include "stdafx.h"

#using <mscorlib.dll>
#include <tchar.h>

using namespace System;

#using <System.Xml.dll>
using namespace System::Xml;

// This is the entry point for this application
int _tmain(void)
{

  XmlDocument* xmlDoc = new XmlDocument();

  try
  {
    xmlDoc->Load("sample.xml");
    System::Console::WriteLine("Document loaded ok." );
  }
  catch (Exception *e)
  {
    System::Console::WriteLine("load problem");
    System::Console::WriteLine(e->Message);
  }

  return 0;
}

The #using statement is really important. Without it, you'll get strange compiler errors like 'Xml' : is not a member of 'System' or 'Xml' : a namespace with this name does not exist. When you're working in C# or VB.NET, there's a Project, Add References menu item that takes care of this for you, but as a C++ programmer, you just do it yourself. You can find the assembly to include in the online help for the class or namespace.

Notice also that this code doesn't use a return value from Load() as the COM approach did. The Load() method will throw an exception if it can't load the XML.

Simple arithmetic with the contents of a document

In my last column, I wrote code to flip through all the elements in a document and determine the total price. Here's the equivalent code the .NET way:

xmlDoc->Load("sample.xml");
double total = 0;
System::Console::WriteLine("Document loaded ok." );
XmlNodeList* items = xmlDoc->GetElementsByTagName("Item");
long numitems = items->Count;
for (int i=0;i<numitems;i++)
{
  XmlNode* item = items->Item(i);
  double price =
    Double::Parse(item->Attributes->GetNamedItem("Price")->
                  get_Value());
  double qty =
    Double::Parse(item->Attributes->GetNamedItem("Quantity")->
                  get_Value());
  total += price * qty;
}
System::Console::WriteLine("Purchase Order total is ${0}",
                           __box(total));

I just added this code in the try block. If you're converting an application from COM and MSXML4 to .NET, notice that the capitalization is quite different, and the functions just return the values you need rather than taking a pointer to an object that they change for you. That makes it simpler to chain long strings of calls together as I've done here. Because Managed C++ is a little pickier about types, I've had to use Double::Parse() to change strings to numbers, and to box up total before passing it to WriteLine().

When this application runs, it prints out:

Document loaded ok.
Purchase Order total is $13.72

If you want to dump the XML out, you just need one line of code:

System::Console::WriteLine(xmlDoc->InnerXml);

InnerXml is a String*, so there's no problem passing it to WriteLine() and no need to go through any intermediate variables.

Where does XML fit in .NET?

Throughout .NET you'll see places where XML belongs. For example, any class can serialize itself to and from XML if you just add the Serializable attribute to it when you declare it.

Here's a sample class that you might want to dump out as XML, or fill from XML:

[Serializable]
public __gc class PurchaseOrder
{
public:
  int customer;
  // other elements to be added
  PurchaseOrder(int id) {customer=id;}
  PurchaseOrder() {customer=0;}
};

You mark the class with the Serializable element. Only public variables can be serialized like this, and the class must have a default constructor. Once you've complied with those restrictions, life gets pretty simple. Here's how to dump out the object to a file:

PurchaseOrder* po = 
   new PurchaseOrder(Int32::Parse(xmlDoc->
     GetElementsByTagName("Customer")->Item(0)->
              Attributes->GetNamedItem("id")->get_Value()));
System::Xml::Serialization::XmlSerializer* ser =
   new System::Xml::Serialization::XmlSerializer(po->GetType());
System::IO::TextWriter* writer = 
       new System::IO::StreamWriter("po.xml");
ser->Serialize(writer,po);
writer->Close();

This would be a lot more readable if you used namespaces, but I wanted you to see the full namespaces associated with the classes I'm using here. The first line, which is very long, roots through the XML document looking for Customer tags, grabs the first one, looks for an attribute called id, and passes its value to the constructor for PurchaseOrder. Then I create a serializer and a writer, and called the Serialize() method of the serializer. The po.xml file comes out like this:

<?xml version="1.0" encoding="utf-8" ?> 
<PurchaseOrder xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <customer>123</customer> 
  </PurchaseOrder>

If PurchaseOrder was a large and complex class, perhaps using one of the .NET collection classes to hold a collection of items, I would still need just this little block of code to write it out as XML: make a serializer and a writer, then call Serialize(). To rehydrate objects from XML, you just do the same work in reverse:

PurchaseOrder* po2 = new PurchaseOrder();
ser = new System::Xml::Serialization::XmlSerializer(po2->GetType());
System::IO::FileStream* fs = 
     new System::IO::FileStream("po.xml", 
                                System::IO::FileMode::Open);
po2 = (PurchaseOrder*) ser->Deserialize(fs);
System::Console::WriteLine("Rehydrated customer is {0}.",
                           __box(po2->customer));

If you're using DataGrids in either ASP.NET or Windows applications, you can load them from a file of XML (wonderful for quick prototypes) or as part of your debugging, have them dump themselves out to XML when you need to. Just make an empty DataSet object, call its ReadXml method to fill it with XML from a file, then make it the data source for your DataGrid and call DataBind(). Or grab the XML from the DataGrid like this:

xmlstring = DataGrid1->DataSource->GetXml();

There's so much more you can do with XML using the .NET class libraries. In just a line or two, you can generate XML, transform from one layout to another, or find subsets of a document. With the schema support in Visual Studio you can generate a schema that matches a sample XML file, or edit XML with Intellisense that knows the schema you're using and reminds you what elements or attributes are allowed where you're typing. XML is at the heart of the .NET revolution, so you should see what it's all about sooner rather than later.

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.

# # #

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date