📄 advanced.html
字号:
<html><body><h1 align='right'><a name='ADVANCED'><img src="3.gif" align="right"hspace="10" width="100" height="100" alt="3"></a>More Mini-XMLProgramming Techniques</h1><p>This chapter shows additional ways to use the Mini-XMLlibrary in your programs.</p><h2><a name='LOAD_CALLBACKS'>Load Callbacks</a></h2><p><a href='#LOAD_XML'>Chapter 2</a> introduced the <ahref='#mxmlLoadFile'><tt>mxmlLoadFile()</tt></a> and <ahref='#mxmlLoadString'><tt>mxmlLoadString()</tt></a> functions.The last argument to these functions is a callback functionwhich is used to determine the value type of each data node inan XML document.</p><p>Mini-XML defines several standard callbacks for simpleXML data files:</p><ul> <li><tt>MXML_INTEGER_CALLBACK</tt> - All data nodes contain whitespace-separated integers.</li> <li><tt>MXML_OPAQUE_CALLBACK</tt> - All data nodes contain opaque strings ("CDATA").</li> <li><tt>MXML_REAL_CALLBACK</tt> - All data nodes contain whitespace-separated floating-point numbers.</li> <li><tt>MXML_TEXT_CALLBACK</tt> - All data nodes contain whitespace-separated strings.</li></ul><p>You can provide your own callback functions for more complexXML documents. Your callback function will receive a pointer tothe current element node and must return the value type of theimmediate children for that element node: <tt>MXML_INTEGER</tt>,<tt>MXML_OPAQUE</tt>, <tt>MXML_REAL</tt>, or <tt>MXML_TEXT</tt>.The function is called <i>after</i> the element and itsattributes have been read, so you can look at the element name,attributes, and attribute values to determine the proper valuetype to return.</p><!-- NEED 2in --><p>The following callback function looks for an attribute named"type" or the element name to determine the value type for itschild nodes:</p><pre> mxml_type_t type_cb(mxml_node_t *node) { const char *type; /* * You can lookup attributes and/or use the * element name, hierarchy, etc... */ type = mxmlElementGetAttr(node, "type"); if (type == NULL) type = node->value.element.name; if (!strcmp(type, "integer")) return (MXML_INTEGER); else if (!strcmp(type, "opaque")) return (MXML_OPAQUE); else if (!strcmp(type, "real")) return (MXML_REAL); else return (MXML_TEXT); }</pre><p>To use this callback function, simply use the name when youcall any of the load functions:</p><pre> FILE *fp; mxml_node_t *tree; fp = fopen("filename.xml", "r"); tree = mxmlLoadFile(NULL, fp, <b>type_cb</b>); fclose(fp);</pre><h2><a name='SAVE_CALLBACKS'>Save Callbacks</a></h2><p><a href='#LOAD_XML'>Chapter 2</a> also introduced the <ahref='#mxmlSaveFile'><tt>mxmlSaveFile()</tt></a>, <ahref='#mxmlSaveString'><tt>mxmlSaveString()</tt></a>, and <ahref='#mxmlSaveAllocString'><tt>mxmlSaveAllocString()</tt></a>functions. The last argument to these functions is a callbackfunction which is used to automatically insert whitespace in anXML document.</p><p>Your callback function will be called up to four times foreach element node with a pointer to the node and a "where" valueof <tt>MXML_WS_BEFORE_OPEN</tt>, <tt>MXML_WS_AFTER_OPEN</tt>,<tt>MXML_WS_BEFORE_CLOSE</tt>, or <tt>MXML_WS_AFTER_CLOSE</tt>.The callback function should return <tt>NULL</tt> if nowhitespace should be added and the string to insert (spaces,tabs, carriage returns, and newlines) otherwise.</p><p>The following whitespace callback can be used to addwhitespace to XHTML output to make it more readable in a standardtext editor:</p><pre> const char * whitespace_cb(mxml_node_t *node, int where) { const char *name; /* * We can conditionally break to a new line * before or after any element. These are * just common HTML elements... */ name = node->value.element.name; if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") || !strcmp(name, "pre") || !strcmp(name, "p") || !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") || !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6")) { /* * Newlines before open and after * close... */ if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE) return ("\n"); } else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul")) { /* * Put a newline before and after list * elements... */ return ("\n"); } else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li")) { /* * Put a tab before <li>'s, * <dd>'s, * and <dt>'s, and a newline after them... */ if (where == MXML_WS_BEFORE_OPEN) return ("\t"); else if (where == MXML_WS_AFTER_CLOSE) return ("\n"); } /* * Return NULL for no added whitespace... */ return (NULL); }</pre><p>To use this callback function, simply use the name when youcall any of the save functions:</p><pre> FILE *fp; mxml_node_t *tree; fp = fopen("filename.xml", "w"); mxmlSaveFile(tree, fp, <b>whitespace_cb</b>); fclose(fp);</pre><!-- NEED 10 --><h2>Custom Data Types</h2><p>Mini-XML supports custom data types via global load and savecallbacks. Only a single set of callbacks can be active at anytime, however your callbacks can store additional information inorder to support multiple custom data types as needed. The<tt>MXML_CUSTOM</tt> node type identifies custom data nodes.</p><p>The load callback receives a pointer to the current data nodeand a string of opaque character data from the XML source withcharacter entities converted to the corresponding UTF-8characters. For example, if we wanted to support a customdate/time type whose value is encoded as "yyyy-mm-ddThh:mm:ssZ"(ISO format), the load callback would look like thefollowing:</p><pre> typedef struct { unsigned year, /* Year */ month, /* Month */ day, /* Day */ hour, /* Hour */ minute, /* Minute */ second; /* Second */ time_t unix; /* UNIX time */ } iso_date_time_t; int load_custom(mxml_node_t *node, const char *data) { iso_date_time_t *dt; struct tm tmdata; /* * Allocate data structure... */ dt = calloc(1, sizeof(iso_date_time_t)); /* * Try reading 6 unsigned integers from the * data string... */ if (sscanf(data, "%u-%u-%uT%u:%u:%uZ", &(dt->year), &(dt->month), &(dt->day), &(dt->hour), &(dt->minute), &(dt->second)) != 6) { /* * Unable to read numbers, free the data * structure and return an error... */ free(dt); return (-1); } /* * Range check values... */ if (dt->month < 1 || dt->month > 12 || dt->day < 1 || dt->day > 31 || dt->hour < 0 || dt->hour > 23 || dt->minute < 0 || dt->minute > 59 || dt->second < 0 || dt->second > 59) { /* * Date information is out of range... */ free(dt); return (-1); } /* * Convert ISO time to UNIX time in * seconds... */ tmdata.tm_year = dt->year - 1900; tmdata.tm_mon = dt->month - 1; tmdata.tm_day = dt->day; tmdata.tm_hour = dt->hour; tmdata.tm_min = dt->minute; tmdata.tm_sec = dt->second; dt->unix = gmtime(&tmdata); /* * Assign custom node data and destroy * function pointers... */ node->value.custom.data = dt; node->value.custom.destroy = free; /* * Return with no errors... */ return (0); }</pre><p>The function itself can return 0 on success or -1 if it isunable to decode the custom data or the data contains an error.Custom data nodes contain a <tt>void</tt> pointer to theallocated custom data for the node and a pointer to a destructorfunction which will free the custom data when the node isdeleted.</p><p>The save callback receives the node pointer and returns an
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -