Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
284
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Exploring Distributed Objects

If the <dialogue> document is provided as input, this program will produce the following output:

Found tag named: dialogue

Found text data:

Found tag named: sentence

Found attribute pair: (speaker=Marni)

Found text data: Let’s go get some ice cream.

Found text data:

Found tag named: sentence

Found attribute pair: (speaker=Scott)

Found text data: After I’m done writing this C++ book.

Found text data:

Note that the white space between tags is being read as a text node.

The Xerces library is exception-savvy. The previous example will abort in the face of an exception other than bad_cast, but a production-quality application would catch and handle Xerces exceptions.

XML Validation

XML is a general-purpose syntax with no predefined tags or semantics of its own. However, that doesn’t mean that any XML application can interpret any XML input. When you write an application that deals with XML, you need to specify the specific type of XML that you can interpret. XML validation allows you to define the specific format of XML that your application allows, including the element names, their organization, and their attributes.

DTD (Document Type Definition)

Document Type Definitions are the original technique for specifying the type of an XML document. The structure of a DTD initially looks like XML itself, but it’s not. Instead of a hierarchical format, DTDs are written as a series of declarations about the document type. The details of DTD creation are beyond the scope of this book. To give you an idea of what a DTD looks like, however, here is the DTD corresponding to the <dialogue> document:

<?xml version=”1.0” encoding=”UTF-8”?> <!ELEMENT dialogue (sentence+)> <!ELEMENT sentence (#PCDATA)>

<!ATTLIST sentence

speaker (Marni | Scott) #REQUIRED

>

Inside of an XML document, you can specify the DTD to which it conforms by including a DOCTYPE assertion at the top of the file:

721

Chapter 24

<?xml version=”1.0”?>

<!DOCTYPE dialogue SYSTEM “dialogue.dtd”> <dialogue>

<sentence speaker=”Marni”>Let’s go get some ice cream.</sentence> <sentence speaker=”Scott”>After I’m done writing this C++ book.</sentence>

</dialogue>

The DOCTYPE assertion requires two parameters. The first is the root element of the document and the second is the location of the DTD file. In this case, the DTD resides on the local system in a file named dialogue.dtd.

Most XML parsing libraries, including Xerces, can validate an XML file against its DTD. That way, you can guarantee that your program will only operate on data that it can interpret.

XML Schema

Validation of XML documents is a great idea, but the DTD format leaves much to be desired. On complex documents, DTDs quickly become unwieldy. They also don’t provide facilities for defining complex types, ordering, or data content. Not to mention the fact that DTDs aren’t even written in XML!

XML Schema is an attempt to provide a more functional way of defining the type of an XML document. XML Schema definitions are vastly more flexible than DTDs, but that added flexibility brings added complexity. There are several excellent books about XML Schema (see Appendix B), so we will again provide only a very simple example:

<?xml version=”1.0” encoding=”UTF-8”?>

<xs:schema xmlns:xs=”http://www.w3.org/2001/XMLSchema”> <xs:element name=”dialogue”>

<xs:complexType>

<xs:sequence>

<xs:element ref=”sentence” maxOccurs=”unbounded”/> </xs:sequence>

</xs:complexType>

</xs:element>

<xs:element name=”sentence”> <xs:complexType>

<xs:simpleContent>

<xs:extension base=”xs:string”>

<xs:attribute name=”speaker” use=”required”> <xs:simpleType>

<xs:restriction base=”xs:NMTOKEN”> <xs:enumeration value=”Marni”/> <xs:enumeration value=”Scott”/>

</xs:restriction>

</xs:simpleType>

</xs:attribute>

</xs:extension>

</xs:simpleContent>

</xs:complexType>

</xs:element>

</xs:schema>

722

Exploring Distributed Objects

Just as with a DTD, you can correlate an XML schema with an XML document. Instead of using a DOCTYPE declaration, you specify the location of the schema within an attribute of the root element:

<?xml version=”1.0”?>

<dialogue xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”

xsi:noNamespaceSchemaLocation=”dialogue.xsd”>

<sentence speaker=”Marni”>Let’s go get some ice cream.</sentence> <sentence speaker=”Scott”>After I’m done writing this C++ book.</sentence>

</dialogue>

Note that the xmlns:xsi attribute specifies that the document is an instance of the XML Schema located at the xsi:noNamespaceSchemaLocation attribute.

Software packages such as xmlspy from Altova Software (www.xmlspy.com) can greatly ease the process of generating and interpreting XML, XML schemas, and DTDs.

Building a Distributed Object with XML

An XML distributed object is simply an object that knows how to output itself as XML and populate itself from XML. In this section, you will turn the Simple class defined earlier into a distributed object using XML serialization.

The XMLSerializable Mix-in Class

In an application that deals with many distributed objects, it is often convenient to have a common parent class for all such objects. The XMLSerializable class, defined in the following example, requires that subclasses implement methods to read themselves from XML and write themselves to XML. This is an example of a mix-in class (see Chapter 25 for further details).

class XMLSerializable

{

public:

virtual std::string toXML() = 0;

virtual void fromXML(const std::string& inXML) = 0;

};

Here is the new Simple class, modified to inherit from XMLSerializable:

class Simple : public XMLSerializable

{

public:

std::string mName; int mPriority;

std::string mData;

723

Chapter 24

virtual std::string toXML();

virtual void fromXML(const std::string& inXML);

};

Implementing XML Serialization

The actual serialization code is aided greatly by the XMLElement class implemented earlier and the use of the Xerces library:

string Simple::toXML()

{

XMLElement simpleElement; simpleElement.setElementName(“simple”);

simpleElement.setAttribute(“name”, mName);

//Convert the int into a string. ostringstream tempStream; tempStream << mPriority;

simpleElement.setAttribute(“priority”, tempStream.str());

//Add the data as a text node. simpleElement.setTextNode(mData);

//Convert the XMLElement into a string.

ostringstream resultStream; resultStream << simpleElement;

return resultStream.str();

}

void Simple::fromXML(const string& inString)

{

static const char* bufID = “simple buffer”;

// Use MemBufInputSource to read the XML content from a string. MemBufInputSource src((const XMLByte*)inString.c_str(),

inString.length(), bufID); XercesDOMParser* parser = new XercesDOMParser(); parser->parse(src);

DOMNode* node = parser->getDocument();

DOMDocument* document = dynamic_cast<DOMDocument*>(node); if (document == NULL) {

delete parser; return;

}

// Document should be the <simple> element. try {

const DOMElement& elementNode =

dynamic_cast<const DOMElement&>(*document->getDocumentElement());

724

Exploring Distributed Objects

// Get the name attribute.

XMLCh* nameKey = XMLString::transcode(“name”);

char* name = XMLString::transcode(elementNode.getAttribute(nameKey)); XMLString::release(&nameKey);

mName = name; XMLString::release(&name);

// Get the priority attribute.

XMLCh* priorityKey = XMLString::transcode(“priority”); char* priorityStr =

XMLString::transcode(elementNode.getAttribute(priorityKey));

XMLString::release(&priorityKey); // Parse the priority number.

istringstream tmpStream(priorityStr); tmpStream >> mPriority;

XMLString::release(&priorityStr);

// Get the data as a text node.

const XMLCh* textData = elementNode.getTextContent(); char* data = XMLString::transcode(textData);

mData = data; XMLString::release(&data);

} catch (bad_cast) {

cerr << “cast exception while parsing Simple object from XML” << endl;

}catch (...) {

cerr << “an unknown error occurred while parsing a Simple object from XML”

<< endl;

}

delete parser;

}

Following is a main() that tests the serialization functionality by creating a Simple object, writing it to XML, then reading that same XML output into a new Simple object. When finished, both objects should be equivalent.

int main(ing argc, char** argv)

{

XMLPlatformUtils::Initialize();

Simple test; test.mName = “myname”; test.mPriority = 7; test.mData = “my data”;

string xmlData = test.toXML();

Simple test2; test2.fromXML(xmlData);

725