<p>The <code>cb</code> argument specifies a function that assigns child (value) node types for each element in the document. The callback can be a function you provide or one of the standard functions provided with Mini-XML. For example, to load the XML file "filename.xml" containing text strings you can use the<code>MXML_OPAQUE_CALLBACK</code> function:</p>
<p>The <code>load_cb</code> argument specifies a function that assigns child (value) node types for each element in the document. The default callback (<code>NULL</code>) supports passing a pointer to an<code>mxml_type_t</code>variable containing the type of value nodes. For example, to load the XML file "filename.xml" containing literal strings you can use:</p>
<p>The last argument to the <code>mxmlLoad</code> functions is a callback function which is used to determine the value type of each data node in an XML document. Mini-XML defines several standard callbacks for simple XML data files:</p>
<ul>
<li><p><code>MXML_INTEGER_CALLBACK</code>: All data nodes contain whitespace-separated integers.</p>
</li>
<li><p><code>MXML_OPAQUE_CALLBACK</code>: All data nodes contain opaque strings with whitespace preserved.</p>
</li>
<li><p><code>MXML_REAL_CALLBACK</code> - All data nodes contain whitespace-separated floating-point numbers.</p>
</li>
<li><p><code>MXML_TEXT_CALLBACK</code> - All data nodes contain whitespace-separated strings.</p>
</li>
</ul>
<p>The <code>load_xxx</code> arguments to the <code>mxmlLoad</code> functions are a callback function and a data pointer which are used to determine the value type of each data node in an XML document. The default (<code>NULL</code>) callback expects the <code>load_cbdata</code> argument to be a pointer to a <code>mxml_type_t</code> variable - if <code>NULL</code> it returns the <code>MXML_TYPE_TEXT</code> type.</p>
<p>You can provide your own callback functions for more complex XML documents. Your callback function will receive a pointer to the current element node and must return the value type of the immediate children for that element node: <code>MXML_TYPE_CUSTOM</code>, <code>MXML_TYPE_INTEGER</code>, <code>MXML_TYPE_OPAQUE</code>, <code>MXML_TYPE_REAL</code>, or <code>MXML_TYPE_TEXT</code>. The function is called <em>after</em> the element and its attributes have been read, so you can look at the element name, attributes, and attribute values to determine the proper value type to return.</p>
<p>The following callback function looks for an attribute named "type" or the element name to determine the value type for its child nodes:</p>
<h2class="title"id="custom-data-types">Custom Data Types</h2>
<p>Mini-XML supports -------------------- data types via per-thread load and save callbacks. Only a single set of callbacks can be active at any time for the current thread, however your callbacks can store additional information in order to support multiple -------------------- data types as needed. The <code>MXML_TYPE_CUSTOM</code> node type identifies -------------------- data nodes.</p>
<p>The <code>mxmlGetCustom</code> function retrieves the -------------------- value pointer for a node.</p>
<p>Mini-XML supports custom data types via per-thread load and save callbacks. Only a single set of callbacks can be active at any time for the current thread, however your callbacks can store additional information in order to support multiple custom data types as needed. The <code>MXML_TYPE_CUSTOM</code> node type identifies custom data nodes.</p>
<p>The <code>mxmlGetCustom</code> function retrieves the custom value pointer for a node.</p>
<p>Custom (<code>MXML_TYPE_CUSTOM</code>) nodes are created using the <code>mxmlNewCustom</code> function or using a -------------------- per-thread load callbacks specified using the <code>mxmlSetCustomHandlers</code> function:</p>
<p>Custom (<code>MXML_TYPE_CUSTOM</code>) nodes are created using the <code>mxmlNewCustom</code> function or using a custom per-thread load callbacks specified using the <code>mxmlSetCustomHandlers</code> function:</p>
<p>The load callback receives a pointer to the current data node and a string of opaque character data from the XML source with character entities converted to the corresponding UTF-8 characters. For example, if we wanted to support a</p>
<p>-------------------- date/time type whose value is encoded as "yyyy-mm-ddThh:mm:ssZ" (ISO format), the load callback would look like the following:</p>
<p>The load callback receives a pointer to the current data node and a string of opaque character data from the XML source with character entities converted to the corresponding UTF-8 characters. For example, if we wanted to support a custom date/time type whose value is encoded as "yyyy-mm-ddThh:mm:ssZ" (ISO format), the load callback would look like the following:</p>
<p>The function itself can return <code>true</code> on success or <code>false</code> if it is unable to decode the -------------------- data or the data contains an error. Custom data nodes contain a <code>void</code> pointer to the allocated -------------------- data for the node and a pointer to a destructor function which will free the -------------------- data when the node is deleted. In this example, we use the standard <code>free</code> function since everything is contained in a single calloc'd block.</p>
<p>The save callback receives the node pointer and returns an allocated string containing the -------------------- data value. The following save callback could be used for our ISO date/time type:</p>
<p>The function itself can return <code>true</code> on success or <code>false</code> if it is unable to decode the custom data or the data contains an error. Custom data nodes contain a <code>void</code> pointer to the allocated custom data for the node and a pointer to a destructor function which will free the custom data when the node is deleted. In this example, we use the standard <code>free</code> function since everything is contained in a single calloc'd block.</p>
<p>The save callback receives the node pointer and returns an allocated string containing the custom data value. The following save callback could be used for our ISO date/time type:</p>
<h2class="title"id="sax-stream-loading-of-documents">SAX (Stream) Loading of Documents</h2>
<p>Mini-XML supports an implementation of the Simple API for XML (SAX) which allows you to load and process an XML document as a stream of nodes. Aside from allowing you to process XML documents of any size, the Mini-XML implementation also allows you to retain portions of the document in memory for later processing.</p>
<p>The <code>mxmlSAXLoadFd</code>, <code>mxmlSAXLoadFile</code>, and <code>mxmlSAXLoadString</code> functions provide the SAX loading APIs:</p>
<p>Each function works like the corresponding <code>mxmlLoad</code> function but uses a callback to process each node as it is read. The callback function receives the node, an event code, and a user data pointer you supply and returns <code>true</code> to continue processing or <code>false</code> to stop:</p>
<p>The <code>mxmlLoadFd</code>, <code>mxmlLoadFile</code>, <code>mxmlLoadFilename</code>, <code>mxmlLoadIO</code>, and <code>mxmlLoadString</code> functions support a SAX callback and associated data. The callback function receives the data pointer you supplied, the node, and an event code and returns <code>true</code> to continue processing or <code>false</code> to stop:</p>
<p>Elements are <em>released</em> after the close element is processed. All other nodes are released after they are processed. The SAX callback can <em>retain</em> the node using the <code>mxmlRetain</code> function. For example, the following SAX callback will retain all nodes, effectively simulating a normal in-memory load:</p>
<p>More typically the SAX callback will only retain a small portion of the document that is needed for post-processing. For example, the following SAX callback will retain the title and headings in an XHTML file. It also retains the (parent) elements like <code><html></code>, <code><head></code>, and <code><body></code>, and processing directives like <code><?xml ... ?></code> and declarations like <code><!DOCTYPE ... ></code>:</p>
<p>The resulting skeleton document tree can then be searched just like one loaded using the <code>mxmlLoad</code> functions. For example, a filter that reads an XHTML document from stdin and then shows the title and headings in the document would look like:</p>
<p>The resulting skeleton document tree can then be searched just like one loaded without the SAX callback function. For example, a filter that reads an XHTML document from stdin and then shows the title and headings in the document would look like:</p>
<li><p>SAX callbacks now return a boolean value.</p>
</li>
<li><p>The <code>mxmlSAXLoadXxx</code> functions have been removed in favor of passing the SAX callback function and data pointers to the <code>mxmlLoadXxx</code> functions.</p>
</li>
<li><p>Node types are now named <code>MXML_TYPE_foo</code> instead of <code>MXML_foo</code>.</p>
</li>
<li><p>Functions that returned <code>0</code> on success and <code>-1</code> on error now return <code>true</code> on success and <code>false</code> on error.</p>
@ -55,14 +55,22 @@ main(int argc, // I - Number of command-line args
*tree,// Element tree
*node;// Node which should be in test.xml
mxml_index_t*ind;// XML index
mxml_type_ttype;// Node type
charbuffer[16384];// Save string
constchar*text;// Text string
boolwhitespace;// Whitespace before text string
staticconstchar*types[]=// Strings for node types
{
"MXML_TYPE_CDATA",
"MXML_TYPE_COMMENT",
"MXML_TYPE_DECLARATION",
"MXML_TYPE_DIRECTIVE",
"MXML_TYPE_ELEMENT",
"MXML_TYPE_INTEGER",
"MXML_TYPE_OPAQUE",
"MXML_TYPE_REAL",
"MXML_TYPE_TEXT"
"MXML_TYPE_TEXT",
"MXML_TYPE_CUSTOM"
};
@ -83,16 +91,16 @@ main(int argc, // I - Number of command-line args
return(1);
}
if(tree->type!=MXML_TYPE_ELEMENT)
if(mxmlGetType(tree)!=MXML_TYPE_ELEMENT)
{
fprintf(stderr,"ERROR: Parent has type %s (%d), expected MXML_TYPE_ELEMENT.\n",tree->type<MXML_TYPE_ELEMENT||tree->type>MXML_TYPE_TEXT?"UNKNOWN":types[tree->type],tree->type);
fprintf(stderr,"ERROR: Parent has type %d(%s), expected MXML_TYPE_ELEMENT.\n",mxmlGetType(tree),types[mxmlGetType(tree)]);
mxmlDelete(tree);
return(1);
}
if(strcmp(tree->value.element.name,"element"))
if(strcmp(mxmlGetElement(tree),"element"))
{
fprintf(stderr,"ERROR: Parent value is \"%s\", expected \"element\".\n",tree->value.element.name);
fprintf(stderr,"ERROR: Parent value is \"%s\", expected \"element\".\n",mxmlGetElement(tree));
mxmlDelete(tree);
return(1);
}
@ -102,11 +110,19 @@ main(int argc, // I - Number of command-line args
@ -132,21 +148,21 @@ main(int argc, // I - Number of command-line args
return(1);
}
if(node->type!=MXML_TYPE_INTEGER)
if(mxmlGetType(node)!=MXML_TYPE_INTEGER)
{
fprintf(stderr,"ERROR: First child has type %s (%d), expected MXML_TYPE_INTEGER.\n",node->type<MXML_TYPE_ELEMENT||node->type>MXML_TYPE_TEXT?"UNKNOWN":types[node->type],node->type);
fprintf(stderr,"ERROR: First child has type %d(%s), expected MXML_TYPE_INTEGER.\n",mxmlGetType(node),types[mxmlGetType(node)]);
mxmlDelete(tree);
return(1);
}
if(node->value.integer!=123)
if(mxmlGetInteger(node)!=123)
{
fprintf(stderr,"ERROR: First child value is %ld, expected 123.\n",node->value.integer);
fprintf(stderr,"ERROR: First child value is %ld, expected 123.\n",mxmlGetInteger(node));
mxmlDelete(tree);
return(1);
}
node=node->next;
node=mxmlGetNextSibling(node);
if(!node)
{
@ -155,21 +171,21 @@ main(int argc, // I - Number of command-line args
return(1);
}
if(node->type!=MXML_TYPE_OPAQUE)
if(mxmlGetType(node)!=MXML_TYPE_OPAQUE)
{
fprintf(stderr,"ERROR: Second child has type %s (%d), expected MXML_TYPE_OPAQUE.\n",node->type<MXML_TYPE_ELEMENT||node->type>MXML_TYPE_TEXT?"UNKNOWN":types[node->type],node->type);
fprintf(stderr,"ERROR: Second child has type %s (%d), expected MXML_TYPE_OPAQUE.\n",mxmlGetType(node)<MXML_TYPE_ELEMENT||mxmlGetType(node)>MXML_TYPE_TEXT?"UNKNOWN":types[mxmlGetType(node)],mxmlGetType(node));
fprintf(stderr,"ERROR: Second child value is \"%s\", expected \"opaque\".\n",node->value.opaque?node->value.opaque:"(null)");
fprintf(stderr,"ERROR: Second child value is \"%s\", expected \"opaque\".\n",mxmlGetOpaque(node)?mxmlGetOpaque(node):"(null)");
mxmlDelete(tree);
return(1);
}
node=node->next;
node=mxmlGetNextSibling(node);
if(!node)
{
@ -178,21 +194,21 @@ main(int argc, // I - Number of command-line args
return(1);
}
if(node->type!=MXML_TYPE_REAL)
if(mxmlGetType(node)!=MXML_TYPE_REAL)
{
fprintf(stderr,"ERROR: Third child has type %s (%d), expected MXML_TYPE_REAL.\n",node->type<MXML_TYPE_ELEMENT||node->type>MXML_TYPE_TEXT?"UNKNOWN":types[node->type],node->type);
fprintf(stderr,"ERROR: Third child has type %s (%d), expected MXML_TYPE_REAL.\n",mxmlGetType(node)<MXML_TYPE_ELEMENT||mxmlGetType(node)>MXML_TYPE_TEXT?"UNKNOWN":types[mxmlGetType(node)],mxmlGetType(node));
mxmlDelete(tree);
return(1);
}
if(node->value.real!=123.4)
if(mxmlGetReal(node)!=123.4)
{
fprintf(stderr,"ERROR: Third child value is %f, expected 123.4.\n",node->value.real);
fprintf(stderr,"ERROR: Third child value is %f, expected 123.4.\n",mxmlGetReal(node));
mxmlDelete(tree);
return(1);
}
node=node->next;
node=mxmlGetNextSibling(node);
if(!node)
{
@ -201,23 +217,25 @@ main(int argc, // I - Number of command-line args
return(1);
}
if(node->type!=MXML_TYPE_TEXT)
if(mxmlGetType(node)!=MXML_TYPE_TEXT)
{
fprintf(stderr,"ERROR: Fourth child has type %s (%d), expected MXML_TYPE_TEXT.\n",node->type<MXML_TYPE_ELEMENT||node->type>MXML_TYPE_TEXT?"UNKNOWN":types[node->type],node->type);
fprintf(stderr,"ERROR: Fourth child has type %s (%d), expected MXML_TYPE_TEXT.\n",mxmlGetType(node)<MXML_TYPE_ELEMENT||mxmlGetType(node)>MXML_TYPE_TEXT?"UNKNOWN":types[mxmlGetType(node)],mxmlGetType(node));
fprintf(stderr,"ERROR: Fourth child value is %d,\"%s\", expected 1,\"text\".\n",node->value.text.whitespace,node->value.text.string?node->value.text.string:"(null)");
fprintf(stderr,"ERROR: Fourth child value is %s,\"%s\", expected true,\"text\".\n",whitespace?"true":"false",text);
mxmlDelete(tree);
return(1);
}
for(i=0;i<4;i++)
{
node=node->next;
node=mxmlGetNextSibling(node);
if(!node)
{
@ -226,9 +244,9 @@ main(int argc, // I - Number of command-line args
return(1);
}
if(node->type!=MXML_TYPE_ELEMENT)
if(mxmlGetType(node)!=MXML_TYPE_ELEMENT)
{
fprintf(stderr,"ERROR: Group child #%d has type %s (%d), expected MXML_TYPE_ELEMENT.\n",i+1,node->type<MXML_TYPE_ELEMENT||node->type>MXML_TYPE_TEXT?"UNKNOWN":types[node->type],node->type);
fprintf(stderr,"ERROR: Group child #%d has type %d(%s), expected MXML_TYPE_ELEMENT.\n",i+1,mxmlGetType(node),types[mxmlGetType(node)]);
mxmlDelete(tree);
return(1);
}
@ -242,9 +260,9 @@ main(int argc, // I - Number of command-line args