## Chapter 29 - XML

## Chapter Goals

- To understand XML elements and attributes
- To understand the concept of an XML parser
- To write programs that load and save XML documents
- To design Document Type Definitions for XML documents

## Chapter 29 - XML

- Extensible Markup Language (XML)
- Describes structure, not appearance
- For transmitting data
- Encode complex data
- Easy to decode
- Parsers widely available

## 29.1.1 Advantages of XML

- E.g., naive encoding of names and salaries:

  > `Mary Miller 64500
  > Jim J. Jones Jr 42000`

- In XML:

  >     <employee>
  >        <name>Mary Miller</name>
  >        <salary>64500</salary>
  >     </employee>
  >     <employee>
  >        <name>Jim J. Jones Jr</name>
  >        <salary>42000</salary>
  >     </employee>

## 29.1.1 Advantages of XML

- Not ambiguous
- Easily read by humans
- Resilient to change
- E.g., add hire date:

  > `Mary Miller 64500 1982`

  - Old code will choke
  - New code won't parse old data

## 29.1.1 Advantages of XML (cont.)

- To add to XML:

  >     <employee>
  >        <name>Mary Miller</name>
  >        <salary>64500</salary>
  >        <year>1982</year>
  >     </employee>

## Quality Tip 29.1

XML is Stricter Than HTML

- Tags are case sensitive
- Every tag must have a matching close tag
  - Or end with `/>`

    > `<img src="hamster.jpeg"/>`

- Attribute values must be enclosed in quotes

## 29.2.1 The Structure of an XML Document

- Document - XML data set
- Should start with header (not an XML element):

  > `<?xml version="1.0"?>`

- Actual data contained in a root element:

  >     <?xml version="1.0"?>
  >     <staff>
  >        more data
  >     </staff>

## 29.2.1 The Structure of an XML Document (cont.)

- An element may have content:

  > `<elementTag optional attributes> content </elementTag>`

  - Content may be:
    - Other elements (element content)
    - Text
    - Both (mixed content)
- Avoid mixed content

## 29.2.1 The Structure of an XML Document (cont.)

- An element may have no content:

  > `<img src="hamster.jpeg"/>`

- May have attributes
  - An attribute has:
    - name (e.g., `src`)
    - value, in single or double quotes (`"hamster.jpeg"`)

## 29.2 Parsing XML Documents

- Parser - reads and analyzes an XML document
- Available in C++, Java, and many other languages
- Two common flavors:
  DOM (Document Object Model)
    Builds parse tree out of entire document
    Easier to use
    Complete overview of the data
  SAX (Simple Access to XML)
    Event-driven; calls user-provided functions on certain events
    More efficient for large documents. Gives client information in bits and pieces

## 29.2 Parsing XML Documents (cont.)

- This chapter describes a DOM parser
- Xerces library (from Apache) - parser API

- Classes to process XML input
- Classes to represent tree structure of document

- Download and instructions at http://xml.apache.org/xerces-c/

## 29.2 Parsing XML Documents (cont.)

- To read a particular type of XML document:
  - Write C++ program, translate XML input into C++ objects
    - Use Xerces library to parse input
    - Examine resulting tree, translate into objects
- Separate program for each type of document
- Parser performs tasks common to all such programs

## 29.2 Parsing XML Documents (cont.)

- `DOM` prefix
- Objects accessed through ptrs
  - Do not `delete`
  - Call `release` method on the builder object when done

## 29.2 Parsing XML Documents (cont.)

- Use `DOMBuilder` object to read XML document from file:

  > `DOMImplementation* implementation
  > = DOMImplementation::getImplementation();
  > DOMBuilder* parser = implementation->createDOMBuilder(
  > DOMImplementation::MODE_SYNCHRONOUS, NULL);`

- 1st call retrieves the default object
  - A factory for classes for reading and writing documents
- 2nd call gets builder object from implementation object

## 29.2 Parsing XML Documents (cont.)

- Use builder to parse document from file:

  > `DOMDocument* doc = parser->parseURI("items.xml");`

- `DOMDocument` describes the tree structure of the document
- To inspect the root element:

  > `DOMNode* root = doc->getDocumentElement();`

- `getDocumentElement` returns DOMElement
- `DOMElement`, along w/`DOMText`, and others, are derived from `DOMNode`

## 29.2 Parsing XML Documents (cont.)

- `getFirstChild` and `getLastChild` methods to retrieve just one child.
  - Return `NULL` if node doesn't exist
- For more children, use `DOMNode::getChildNodes`
  - Returns `DOMNodeList`
- E.g., item list:

## 29.2 Parsing XML Documents (cont.)

>     <?xml version="1.0"?>
>     <items>
>        <item>
>           <product>
>              <description>Ink Jet Refill Kit</description>
>              <price>29.95</price>
>           </product>
>           <quantity>8</quantity>
>        </item>
>        <item>
>           <product>
>              <description>4-port Mini Hub</description>
>              <price>19.95</price>
>           </product>
>           <quantity>4</quantity>
>        </item>
>     </items>

## 29.2 Parsing XML Documents (cont.)

- Document is a tree
- `Items` element is root

## 29.2 Parsing XML Documents (cont.)

- `getChildNodes` on root produces list of two `item` elements
- To traverse `DOMNodeList`:
  - `getLength` - # of nodes in list
  - `item` w/index to get a item
- So,

  > `DOMNodeList* nodes = root->getChildNodes();
  > int i = . . .;
  > // A # between 0 and nodes->getLength() - 1
  > DOMNode* child_node = nodes->item(i);`

## 29.2 Parsing XML Documents (cont.)

- Parser keeps all white space, by default
- Stored in nodes
- If you didn't mix content, easy to ignore:

- If current element contains elements, skip all non-element child nodes:

  >     for (int i = 0; i < nodes->getLength(); i++)
  >     {
  >        DOMNode* child_node = nodes->item(i);
  >        DOMElement* child_element
  >           = dynamic_cast<DOMElement*>(child_node);
  >        if (child_element != NULL)
  >        {
  >           // Do something with child_element
  >           . . .
  >        }
  >     }

## 29.2 Parsing XML Documents (cont.)

- Given an element, get the name (e.g., `price`):

  > `DOMElement* price_element = . . .;
  > XMLCh* name = price_element->getTagName();
  > // Returns a tag name, such as price`

  - Returns Unicode, but not in `wstring`

## 29.2 Parsing XML Documents (cont.)

- If ASCII suffices, use following function to convert:

  >     string XMLCh_to_string(const XMLCh* in)
  >     {
  >        char* s = XMLString::transcode(in);
  >        string r(s);
  >        XMLString::release(&s);
  >        return r;
  >     }

  - `XMLString::transcode` converts `XMLCh` array into `char` array
  - `XMLString::release` recycles memory
- Localize library idiosyncrasies w/helper functions

## 29.2 Parsing XML Documents (cont.)

- To retrieve attributes by name:
  - Supply name as an `XMLCh` array
  - `XMLCh` array must be deleted
- Wrap `getAttribute` method in a help function:

## 29.2 Parsing XML Documents (cont.)

>     string get_attribute(DOMElement* element, string name)
>     {
>        XMLCh* xname = new XMLCh[name.length() + 1];
>        XMLString::transcode(name.c_str(),
>           xname, name.length());
>        XMLCh* xvalue = element->getAttribute(xname);
>        delete xname;
>        return XMLCh_to_string(xvalue);
>     }

- E.g., get the `currency` attribute

> `string attribute_value = get_attribute(
> price_element, "currency");`

## 29.2 Parsing XML Documents (cont.)

- Use `DOMNamedNodeMap` to iterate over attributes
- Returned by `getAttributes`:

  > `DOMNamedNodeMap* attributes = price_element->getAttributes();`

- Use `item` method to get a particular attribute:

  > `int i = . . .;
  > // # between 0 and attributes->getLength() - 1
  > DOMNode* attribute_node = attributes->item(i);
  > XMLCh* attribute_name = attribute_node->getNodeName();
  > // For example, "currency"
  > XMLCh* attribute_value = attribute_node.getNodeValue();
  > // For example, "USD"`

## 29.2 Parsing XML Documents (cont.)

- Text node stored in `DOMText`
- If no mixed content, should be only child
- Use `getFirstChild` to retrieve
- Use `getData` to read the text

> `DOMNode* price_element = . . .;
> DOMText* price_data = dynamic_cast<DOMText*>(
> price_element->getFirstChild());
> string price_string = XMLCh_to_string(price_data->getData());
> // For example, "24.95"
> double price = string_to_double(price_string);
> // For example, 24.95`

## 29.2 Parsing XML Documents - Parser example

- Create a helper function for each XML element
- `description`, `price`, and `quantity` simple, so parsed inline
- `main` calls `XMLPlatformUtils::Initialize`; necessary to use Xerces
- Program:
  - Parses `items.xml`, description of a list of product items
  - Produces a vector of `Item` objects
- Uses `item.h`, `item.cpp`, `product.h`, and `product.cpp` from (chptr 13)

## 29.2 Parsing XML Documents (parser.cpp)

## Productivity Hint 29.1

Helper Functions in an XML Parser

- Given an XML document of a particular structure
- For each element:
  - Assign a (new or existing) class or vector (for sequences of the same type)
  - Provide a helper function of this form:

## Productivity Hint 29.1 (cont.)

>     ClassForElement getElementName(DOMNode* e)
>     {
>        DOMNodeList* children = e->getChildNodes();
>        for (int i = 0; i < children->getLength(); i++)
>        {
>           DOMNode* child_node = children->item(i);
>           DOMElement* child_element
>              = dynamic_cast<DOMElement*>(child_node);
>           if (child_element != NULL)
>           {
>              Get value of child element
>           }
>        }
>        Use the child element values to construct and
>        return a ClassForElement object
>     }

## Productivity Hint 29.1 (cont.)

- If a child element doesn't contain text, call its `getChildElementName` function
- If it does, then read the element data:

  >     DOMText* text_node = dynamic_cast<DOMText*>(
  >        child_element->getFirstChild());
  >     string data = XMLCh_to_string(text_node->getData());

- Convert strings to numbers, as necessary
- Implement functions bottom-up

## 29.3 Creating XML Documents

- Avoid simple print statements - easy to introduce an error
- Build a `DOMDocument` object
- Still use `DOMImplementation` object to create parser:

  > `DOMImplementation* implementation
  > = DOMImplementation::getImplementation();
  > DOMDocument* doc = implementation->createDocument();
  > doc->setStandalone(true);
  > // An empty document`

- "standalone" flag indicates lack of document type definition

## 29.3 Creating XML Documents (cont.)

- Use `createElement` and `createTextNode` methods of `DOMDocument`
- Inputs are an `XMLCh` array
- So, wrap above methods, to take `string`s
- Wrapper for `DOMDocument::createElement`:

  >     DOMElement* create_element(DOMDocument* doc, string name)
  >     {
  >        XMLCh* xname = new XMLCh[name.length() + 1];
  >        XMLString::transcode(name.c_str(), xname, name.length());
  >        DOMElement* r = doc->createElement(xname);
  >        delete xname;
  >        return r;
  >     }

## 29.3 Creating XML Documents (cont.)

- To insert text node:
  - Convert text to `XMLCh` array
  - Wrap in a text node
  - Create an element node
  - Set text node as child

## 29.3 Creating XML Documents (cont.)

>     DOMElement* create_text_element(DOMDocument* doc,
>        string name, string text)
>     {
>        XMLCh* xtext = new XMLCh[text.length() + 1];
>        XMLString::transcode(text.c_str(), xtext, text.length());
>        DOMText* textNode = doc->createTextNode(xtext);
>        delete xtext;
>        DOMElement* r = create_element(doc, name);
>        r->appendChild(textNode);
>        return r;
>     }

## 29.3 Creating XML Documents (cont.)

- Build DOM tree to save to an XML document, for persistence or transmission
- Set attributes w/the `setAttribute` method
- E.g.,

  > `XMLCh xname = . . .;
  > XMLCh xvalue = . . .;
  > price_element->setAttribute(xname, xvalue);`

## 29.3 Creating XML Documents (cont.)

- Start with root, then add children
- Consider the simple tree:

## 29.3 Creating XML Documents (cont.)

- First, create elements:

  >     DOMElement* itemElement = create_element(doc, "item");
  >     DOMElement* product_element = create_element(doc, "product");
  >     DOMElement* description_element = create_text_element(doc,
  >        "description", "Ink Jet Refill Kit");
  >     DOMElement* price_element = create_text_element(doc,
  >        "price", "29.95");
  >     DOMElement* quantity_element
  >     	 = create_text_element(doc, "quantity", "8");

## 29.3 Creating XML Documents (cont.)

- Then add elements to the document:

  >     doc->appendChild(item_element);
  >     item_element->appendChild(product_element);
  >     item_element->appendChild(quantity_element);
  >     product_element->appendChild(description_element);
  >     product_element->appendChild(price_element);

## 29.3 Creating XML Documents (cont.)

- Organize these steps into helper functions
- Sample program (later) uses:

  >     DOMElement* create_product(DOMDocument* doc, const Product& p)
  >     DOMElement* create_item(DOMDocument* doc, const Item& anItem)
  >     DOMElement* create_item_list(DOMDocument* doc,
  >     	 const vector<Item>& items)

## 29.3 Creating XML Documents (cont.)

- Obtain `DOMWriter` object from the `DOMImplementation` object:

  > `DOMWriter* writer = implementation->createDOMWriter();
  > writer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true);`

- "Pretty print" aligns elements
- Default is w/out whitespace

## 29.3 Creating XML Documents (cont.)

- A "format target" is needed
- To print to `stdout`:

  > `XMLFormatTarget* out = new StdOutFormatTarget();
  > writer->writeNode(out, *doc);`

- To write to a local file:

  > `XMLCh* filename = . . .; XMLFormatTarget* out = new LocalFileFormatTarget(filename); writer->writeNode(out, *doc);`

- `writeNode` takes a `DOMDocument`, not a ptr

## 29.3 Creating XML Documents - Sample

- Sample program uses `item.h`, `item.cpp`, `product.h`, and `product.cpp` from (chptr 13)
- Added `get_product` and `get_quantity` to `Item`

## 29.3 Creating XML Documents (`builder.cpp`)

## 29.4 Document Type Definitions

- DTD - set of rules for correctly formed documents of a particular type
- E.g., consider the `items` document:
  - `items` denotes a sequence of `item` elements
  - `item` contains a `product` and a `quantity`
  - `product` contains a `description` and a `price`
  - Each of these elements contains descriptive text
- DTD formalizes this description

## 29.4 Document Type Definitions (cont.)

- Sequence of rules that describe:
  - Legal attributes for each element type
  - Legal child elements for each element type
- Legal child elements are described by an ELEMENT rule:

  > `<!ELEMENT items (item*)>`

  `items` must contain 0 or more `item` elements
- Delimited by `<!. . .>`
- Contains element whose children are to be constrained
- Followed by description of children

## 29.4 Document Type Definitions (cont.)

- Definition of `item` node:

  > `<!ELEMENT item (product, quantity)>`

- Children of `item` must be a `product` node, followed by a `quantity` node
- `product` is similar:

  > `<!ELEMENT product (description, price)>`

## 29.4 Document Type Definitions (cont.)

- The three remaining node types:

  > `<!ELEMENT quantity (#PCDATA)>
  > <!ELEMENT description (#PCDATA)>
  > <!ELEMENT price (#PCDATA)>`

- `#PCDATA` refers to text; parsable character data

## 29.4 Document Type Definitions (cont.)

- Certain characters need to be encoded:

  Character | Encoding | Name
  --- | --- | ---
  < | &lt; | Less than (left angle bracket)
  > | &gt; | Greater than (right angle bracket)
  & | &amp; | Ampersand
  ' | &apos; | Apostrophe
  " | &quot; | Quotation mark

## 29.4 Document Type Definitions (cont.)

- Expressions to describe children:

  Rule Description | Element Content
  --- | ---
  `EMPTY` | No children allowed
  (E*) | Any sequence of 0 or more elements E
  (E+) | Any sequence of 1 or more elements E
  (E?) | Optional element E (0 or 1)
  (E1, E2, . . .) | Element E1, followed by E2, ...
  (E1|E2|. . .) | Element E1 or E2 or ...
  (`#PCDATA`) | Text only (parsable)
  (`#PCDATA`|E1|E2|. . .)* | Any sequence of text and elements E1, E2, ..., in any order
  `ANY` | Any children allowed

## 29.4 Document Type Definitions (cont.)

- Flowchart describing the regular expressions:

## 29.4 Document Type Definitions (cont.)

- Expressions can be combined:

  > `<!ELEMENT section (title, (paragraph | (image, title?))+)>`

- Defines an element `section` whose children are:
  1. A `title` element
  2. A sequence of one or more of:
     - `paragraph` elements
     - `image` elements followed by optional `title` elements
- Avoid mixed content for DTDs
- Avoid `ANY` type in DTDs

## 29.4 Document Type Definitions (cont.)

- To describe an attribute:

  > `<!ATTLIST Element Attribute Type Default>`

- Common attribute type descriptions:

Type Description | Attribute Type
--- | ---
`CDATA` | Any character data
(N1|N2|. . .) | One of N1, N2, ...

- `CDATA` much like `#PCDATA` (encode same characters)

## 29.4 Document Type Definitions (cont.)

- To specify finite choices:

  > `<!ATTLIST price currency (USD | EUR | JPY) #REQUIRED>`

- Choices may contain letters, numbers, `-`, `_` or `.`
- `currency` attribute must be specified
- Attribute Defaults:

Default Declaration | Explanation
--- | ---
`#REQUIRED` | Attribute is required
`#IMPLIED` | Attribute is optional
N | Default attribute, used if attribute not specified
`#FIXED` N | Attribute must either be unspecified or contain this value

## 29.4 Document Type Definitions (cont.)

- `currency` attribute is optional:

  > `<!ATTLIST price currency CDATA #IMPLIED>`

  Some default currency implicitly assumed
- Better to explicitly supply default:

  > `<!ATTLIST price currency CDATA "USD">`

- `#FIXED` not often used

## Advanced Topic 29.1

The XML Schema Specification

- DTDs don't specify type of data
- E.g., can't constrain an element to an int, or float, or date
- XML Schema Definition currently being developed (2004)
- Addresses this problem
- E.g.:

  > `<xsd:element name="quantity" type="xsd:integer"/>`

## Advanced Topic 29.1

- Some built-in types

XML Schema Type | Meaning
--- | ---
`string` | Any string
`integer` | An integer
`double` | A floating-point number
`boolean` | False or True
`date` | E.g., 2001-01-04
`time` | E.g., 05:43

## Advanced Topic 29.1 (cont.)

- Allows you to define complex types:

  >     <xsd:complexType name="Address">
  >        <xsd:sequence>
  >           <xsd:element name="name" type="xsd:string"/>
  >           <xsd:element name="company" type="xsd:string"/>
  >           <xsd:element name="street" type="xsd:string"/>
  >           <xsd:element name="city" type="xsd:string"/>
  >           <xsd:element name="state" type="xsd:string"/>
  >           <xsd:element name="zip" type="xsd:string"/>
  >        </xsd:sequence>
  >     </xsd:complexType>

## Advanced Topic 29.1 (cont.)

- To be used:

  > `<xsd:element name="shipto" type="Address"/>
  > <xsd:element name="billto" type="Address"/>`

## 29.5 Parsing with DTDs

- Good idea for all XML documents to reference a DTD
  - Parser can check for errors
  - Be more intelligent about parsing
- 2 ways to reference a DTD:
  1. Inline DTD in document
  2. Refer to external DTD
- DTD introduced w/the `DOCTYPE` declaration

## 29.5 Parsing with DTDs (cont.)

- If DTD is inline:

  > `<!DOCTYPE rootElement [rules]>`

- E.g., an item list:

## 29.5 Parsing with DTDs (cont.)

- Store complex DTDs externally
- Use `SYSTEM` keyword to indicate external location
- For a local file:

  > `<!DOCTYPE items SYSTEM "items.dtd">`

- For a page on the Web:

  > `<!DOCTYPE items
  > SYSTEM "http://www.mycompany.com/dtds/items.dtd">`

## 29.5 Parsing with DTDs (cont.)

- Common DTDs can use the `PUBLIC` keyword to indicate an identifier:

  >     <!DOCTYPE web-app
  >        PUBLIC
  >        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  >        "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

- The parser won't retrieve a DTD it already knows from its identifier

## 29.5 Parsing with DTDs (cont.)

- Validate documents that have a DTD
  - Parser checks children and elements
  - Reports errors
- To turn validation on in Xerces:

  > `parser->setFeature(XMLUni::fgDOMValidation, true);`

## 29.5 Parsing with DTDs (cont.)

- Register an error handler to have errors reported
- Handler is a class inherited from `DOMErrorHandler`
- Override method:

  > `bool handleError(const DOMError& domError)`

  - Should log error
  - Return `true` if non-fatal
  - Sample program defines `SimpleErrorHandler`
- To install the handler:

  > `DOMErrorHandler* handler = new SimpleErrorHandler();
  > parser->setErrorHandler(handler);`

## 29.5 Parsing with DTDs (cont.)

- With a DTD, parser can strip out whitespace for you
- To get this behavior:

  >     parser->setFeature(XMLUni::fgDOMWhitespaceInElementContent,
  >        false);

- Parser can fill in default attributes:

  >     XMLCh* attribute_value
  >        = price_element->getAttribute("currency");
  >        // Gets "USD" if no currency specified

## 29.5 Parsing with DTDs (cont.)

- The following program is simpler than the one from sect. 29.2
- Parses `items.xml` using `items.dtd`
  (Note: might not actually display in a browser. Check source)
- Uses the same files: `item.h`, `item.cpp`, `product.h`, and `product.cpp`

## 29.5 Parsing with DTDs (parser2.cpp)

## Chapter Summary

1. XML allows you to encode complex data, independent from any programming language, in a form that a recipient can parse easily, and that is resilient to change
2. An XML data set is called a document. It starts out with a header and contains elements and text. An element can contain text, subelements, or both (mixed content). For data descriptions, avoid mixed content. Elements can have attributes. Use attributes to describe how to interpret the element content
3. A parser is a program that reads a document, checks whether it is syntactically correct, and takes some action as it processes the document. There are two kinds of XML parsers. SAX fires events as it analyzes a document. DOM builds a document tree

## Chapter Summary

4. A DOMDocument object consists of nodes that are represented by objects of the DOMNode class or one of its derived classes. The most important derived classes are DOMElement and DOMText
5. A DTD is a sequence of rules that describe the legal child elements and attributes for each element type. An XML document can contain its DTD or refer to a DTD that is stored elsewhere. When referencing an external DTD, you must supply a file name or URL
6. It is best to use DTDs with XML files. When using DTDs, you should tell the parser to validate the document and to ignore white space. This greatly simplifies the implementation of the parser functions