Author: Amin Tahmasebi
Release Date: 2024
License: ISC License
This XML library in C provides a comprehensive and easy-to-use API for parsing, creating, modifying, and traversing XML documents. It is designed to be lightweight, efficient, and fully cross-platform, making it suitable for embedded systems, desktop applications, and server environments. The library abstracts the complexity of XML manipulation, offering intuitive functions to work with XML elements, attributes, and text content.
- XML Parsing from File or String: Parse XML content from files or strings to easily load and work with XML documents.
- Element Creation and Manipulation: Create new XML elements, set text content, and modify or add attributes.
- Tree Traversal: Traverse XML trees to access parent and child elements, as well as siblings.
- Document Serialization: Convert XML documents back to a string or save them to a file.
- Cross-Platform: Compatible with Linux, Windows, and other platforms.
- Doxygen Documentation: Fully documented using Doxygen for clear and comprehensive guidance on each function.
To use this library, include xml.h
in your project and compile the source files with your C compiler. For GCC, the following command can be used:
gcc -std=c17 -O3 -march=native -flto -funroll-loops -Wall -Wextra -pedantic -s -o main ./main.c ./xml/xml.c
The documentation includes detailed descriptions of all the functions provided by the library, along with usage examples. It covers basic XML operations such as parsing XML files, creating elements, and traversing nodes in an XML document.
- Purpose: Parses an XML file and returns an
XmlDocument
object. The XML file is loaded, parsed, and converted into an internal structure that represents the document. - Parameters:
filename
: The path to the XML file to be parsed.
- Returns:
- An
XmlDocument
object representing the parsed XML structure. ReturnsNULL
if the file cannot be read or parsed.
- An
- Purpose: Parses an XML string and returns an
XmlDocument
object. This function is useful for working with XML content stored in memory. - Parameters:
xml_content
: A string containing the XML data to be parsed.
- Returns:
- An
XmlDocument
object representing the parsed XML content. ReturnsNULL
if the parsing fails.
- An
- Purpose: Creates a new XML document with a specified root element. The root element is the first element in the document.
- Parameters:
root_element_name
: The name of the root element to be created.
- Returns:
- A new
XmlDocument
object with the specified root element.
- A new
- Purpose: Creates a new XML element within the document. The element is not automatically added to the document and must be appended to a parent node.
- Parameters:
doc
: The XML document where the element will belong.tag_name
: The tag name of the element to be created.
- Returns:
- An
XmlNode
representing the newly created element.
- An
- Purpose: Retrieves the root element of the XML document. This is typically the top-level element that contains all other elements.
- Parameters:
doc
: The document from which to retrieve the root element.
- Returns:
- The
XmlNode
representing the root element, orNULL
if no root is found.
- The
- Purpose: Finds the first child element with a matching tag name. This function searches the immediate children of the specified root node.
- Parameters:
root
: The root node where the search begins.tag_name
: The tag name to search for.
- Returns:
- The
XmlNode
representing the first found element with the matching tag name, orNULL
if not found.
- The
- Purpose: Retrieves a specific sub-element based on a variable argument list of tag names. This allows for traversing a path of nested elements.
- Parameters:
root
: The root node where the search begins....
: A variable argument list representing the sequence of tag names to follow.
- Returns:
- The
XmlNode
at the specified path, orNULL
if the path is invalid.
- The
- Purpose: Appends a child element to a parent node, making it a sub-element of the parent.
- Parameters:
parent
: The parent node to which the child will be appended.child
: The child node to be appended.
- Purpose: Sets the text content of an XML element. If the element already has text, it will be replaced.
- Parameters:
element
: The XML element whose text will be set.text
: The text content to assign to the element.
- Purpose: Sets an attribute for an XML element. If the attribute already exists, its value will be updated; otherwise, a new attribute will be added.
- Parameters:
element
: The element to which the attribute will be added.name
: The name of the attribute.value
: The value of the attribute.
- Purpose: Retrieves the text content of an XML element.
- Parameters:
element
: The XML element from which to retrieve the text.
- Returns:
- The text content of the element, or
NULL
if the element has no text.
- The text content of the element, or
- Purpose: Retrieves the value of an attribute from an XML element.
- Parameters:
element
: The XML element to retrieve the attribute from.name
: The name of the attribute to retrieve.
- Returns:
- The value of the attribute, or
NULL
if the attribute is not found.
- The value of the attribute, or
- Purpose: Retrieves the tag name of a given XML node. This is useful for identifying the element type, allowing you to access or manipulate elements based on their tag names.
- Parameters:
node
: The node whose tag name is being requested.
- Returns:
- The tag name of the node, or
NULL
if the node has no tag name.
- The tag name of the node, or
- Purpose: Converts an XML document to a string format, which can then be printed or saved to a file.
- Parameters:
doc
: The XML document to convert.
- Returns:
- A string representing the XML document. The caller is responsible for freeing the string when done.
- Purpose: Saves an XML document to a file. The document is converted to its string representation and written to the specified file.
- Parameters:
doc
: The XML document to save.filename
: The name of the file to save the document to.
- Returns:
1
if the document was saved successfully,0
if there was an error.
- Purpose: Removes a node and its subtags from the document without deallocating memory. This is useful when you want to move or manipulate a node before deletion.
- Parameters:
node
: The node to be removed.
- Purpose: Frees the memory used by the entire XML document, including all nodes and attributes.
- Parameters:
doc
: The document to deallocate.
- **
void xml_deallocate_node(XmlNode* node)
- Purpose: Frees the memory used by a specific XML node and its children, but does not affect other parts of the document.
- Parameters:
node
: The node to deallocate.
- Purpose: Returns the last error message encountered while parsing the document. If no error occurred, it returns an empty string.
- Parameters:
doc
: The document from which to retrieve the error message.
- Returns:
- A string describing the last error, or an empty string if no error occurred.
- Purpose: Retrieves processing instructions for the XML document based on the specified target. Processing instructions provide directions for applications processing the document.
- Parameters:
doc
: The document containing the processing instructions.target
: The target of the processing instruction to retrieve.
- Returns:
- A null-terminated array of strings representing the processing instructions, or
NULL
if none are found.
- A null-terminated array of strings representing the processing instructions, or
#include "xml/xml.h"
#include "fmt/fmt.h"
int main() {
XmlDocument* doc = xml_parse_file("sample.xml");
XmlNode* root = xml_get_root(doc);
if (root) {
fmt_printf("Root element: %s\n", xml_get_tag_name(root));
XmlNode* author = xml_find_element_by_tag(root, "author");
if (author) {
fmt_printf("Author: %s\n", xml_get_element_text(author));
}
}
xml_deallocate_document(doc);
return 0;
}
Result:
<?xml version="1.0"?>
<catalog>
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications with XML.</description>
</catalog>
Root element: catalog
Author: Gambardella, Matthew
#include "xml/xml.h"
#include "fmt/fmt.h"
int main() {
XmlDocument* doc = xml_create_document("catalog");
XmlNode* book = xml_create_element(doc, "book");
fmt_printf("Created book node at %p\n", book);
fmt_printf("Appending book node to root at %p\n", xml_get_root(doc));
// Append the 'book' element to the root node
if (book != xml_get_root(doc)) {
xml_append_child(xml_get_root(doc), book);
}
else {
fmt_printf("Error: Attempting to append a node to itself or its parent.\n");
xml_deallocate_document(doc);
return 1;
}
// Set attributes and text for the 'book' node
xml_set_element_attribute(book, "id", "bk101");
xml_set_element_text(book, "XML Developer's Guide");
xml_save_to_file(doc, "./output.xml");
xml_deallocate_document(doc);
return 0;
}
Result:
Generated XML:
<catalog>
<book id="bk101">XML Developer's Guide</book>
</catalog>
#include "xml/xml.h"
#include "fmt/fmt.h"
int main() {
XmlDocument* doc = xml_parse_string("<?xml version='1.0'?><catalog><book id='bk102'>C Programming</book></catalog>");
if (!doc) {
fmt_printf("Failed to parse document.\n");
return 1;
}
XmlNode* root = xml_get_root(doc);
if (!root) {
fmt_printf("Failed to get root element.\n");
xml_deallocate_document(doc);
return 1;
}
fmt_printf("Root internal node pointer: %p\n", root->internal_node);
if (root->tag_name) {
fmt_printf("Root tag name assigned correctly: %s\n", root->tag_name);
}
else {
fmt_printf("Root tag name is empty or not assigned.\n");
}
XmlNode* book = xml_find_element_by_tag(root, "book");
if (book) {
const char* id = xml_get_element_attribute(book, "id");
if (id) {
fmt_printf("Book ID: %s\n", id);
}
}
else {
fmt_printf("Book element not found.\n");
}
xml_deallocate_document(doc);
return 0;
}
Result:
Root internal node pointer: 000001fe02512490
Root tag name assigned correctly: catalog
Book ID: bk102
#include <stdlib.h>
#include "xml/xml.h"
#include "fmt/fmt.h"
int main() {
XmlDocument* doc = xml_parse_string("<?xml version='1.0'?><catalog><book>Programming in C</book></catalog>");
char* xml_string = xml_to_string(doc);
if (xml_string) {
fmt_printf("XML as string:\n%s\n", xml_string);
free(xml_string);
}
xml_deallocate_document(doc);
return 0;
}
Result:
XML as string:
<catalog>
<book>Programming in C</book>
</catalog>
#include "xml/xml.h"
#include "fmt/fmt.h"
int main() {
XmlDocument* doc = xml_create_document("catalog");
XmlNode* book1 = xml_create_element(doc, "book");
xml_append_child(xml_get_root(doc), book1);
xml_set_element_attribute(book1, "id", "bk101");
xml_set_element_text(book1, "XML Developer's Guide");
XmlNode* book2 = xml_create_element(doc, "book");
xml_append_child(xml_get_root(doc), book2);
xml_set_element_attribute(book2, "id", "bk102");
xml_set_element_text(book2, "Advanced C Programming");
xml_save_to_file(doc, "catalog.xml");
xml_deallocate_document(doc);
return 0;
}
Result:
Generated XML:
<catalog>
<book id="bk101">XML Developer's Guide</book>
<book id="bk102">Advanced C Programming</book>
</catalog>
#include <stdlib.h>
#include <string.h>
#include "fmt/fmt.h"
#include "xml/xml.h"
int main() {
XmlDocument* doc = xml_parse_file("./sources/xml_sample.xml");
if (doc == NULL) {
fmt_fprintf(stderr, "Failed to parse the XML file.\n");
return 1;
}
XmlNode* root = xml_get_root(doc);
if (root == NULL) {
fmt_fprintf(stderr, "Failed to get root element.\n");
xml_deallocate_document(doc);
return 1;
}
XmlNode* book = xml_find_element_by_tag(root, "book");
if (book == NULL) {
fmt_fprintf(stderr, "Failed to find <book> element.\n");
xml_deallocate_document(doc);
return 1;
}
XmlNode* author = xml_find_element_by_tag(book, "author");
if (author != NULL) {
const char* tag_name = xml_get_tag_name(author);
fmt_printf("Author node found, tag name: %s\n", tag_name ? tag_name : "Unknown");
const char* author_name = xml_get_element_text(author);
if (author_name != NULL && strlen(author_name) > 0) {
fmt_printf("Author: %s\n", author_name);
}
else {
fmt_printf("Author node found, but no text available.\n");
}
}
else {
fmt_printf("Author node not found.\n");
}
XmlNode* title = xml_find_element_by_tag(book, "title");
if (title != NULL) {
xml_set_element_text(title, "Updated XML Developer's Guide");
}
xml_set_element_attribute(book, "updated", "yes");
char* xml_string = xml_to_string(doc);
if (xml_string != NULL) {
fmt_printf("Updated XML:\n%s\n", xml_string);
free(xml_string);
}
if (!xml_save_to_file(doc, "./updated_xml_sample.xml")) {
fmt_fprintf(stderr, "Failed to save the updated XML file.\n");
}
else {
fmt_printf("Updated XML saved to 'updated_xml_sample.xml'.\n");
}
xml_deallocate_document(doc);
return 0;
}
Result
Author node found, tag name: author
Author: Gambardella, Matthew
Updated XML:
<catalog>
<book id="bk101" updated="yes">
<author>Gambardella, Matthew</author>
<title>Updated XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.</description>
</book>
<book id="bk104">
<author>Corets, Eva</author>
<title>Oberon's Legacy</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2001-03-10</publish_date>
<description>In post-apocalypse England, the mysterious
agent known only as Oberon helps to create a new life
for the inhabitants of London. Sequel to Maeve
Ascendant.</description>
</book>
<book id="bk105">
<author>Corets, Eva</author>
<title>The Sundered Grail</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2001-09-10</publish_date>
<description>The two daughters of Maeve, half-sisters,
battle one another for control of England. Sequel to
Oberon's Legacy.</description>
</book>
<book id="bk106">
<author>Randall, Cynthia</author>
<title>Lover Birds</title>
<genre>Romance</genre>
<price>4.95</price>
<publish_date>2000-09-02</publish_date>
<description>When Carla meets Paul at an ornithology
conference, tempers fly as feathers get ruffled.</description>
</book>
<book id="bk107">
<author>Thurman, Paula</author>
<title>Splish Splash</title>
<genre>Romance</genre>
<price>4.95</price>
<publish_date>2000-11-02</publish_date>
<description>A deep sea diver finds true love twenty
thousand leagues beneath the sea.</description>
</book>
<book id="bk108">
<author>Knorr, Stefan</author>
<title>Creepy Crawlies</title>
<genre>Horror</genre>
<price>4.95</price>
<publish_date>2000-12-06</publish_date>
<description>An anthology of horror stories about roaches,
centipedes, scorpions and other insects.</description>
</book>
<book id="bk109">
<author>Kress, Peter</author>
<title>Paradox Lost</title>
<genre>Science Fiction</genre>
<price>6.95</price>
<publish_date>2000-11-02</publish_date>
<description>After an inadvertant trip through a Heisenberg
Uncertainty Device, James Salway discovers the problems
of being quantum.</description>
</book>
<book id="bk110">
<author>O'Brien, Tim</author>
<title>Microsoft .NET: The Programming Bible</title>
<genre>Computer</genre>
<price>36.95</price>
<publish_date>2000-12-09</publish_date>
<description>Microsoft's .NET initiative is explored in
detail in this deep programmer's reference.</description>
</book>
<book id="bk111">
<author>O'Brien, Tim</author>
<title>MSXML3: A Comprehensive Guide</title>
<genre>Computer</genre>
<price>36.95</price>
<publish_date>2000-12-01</publish_date>
<description>The Microsoft MSXML3 parser is covered in
detail, with attention to XML DOM interfaces, XSLT processing,
SAX and more.</description>
</book>
<book id="bk112">
<author>Galos, Mike</author>
<title>Visual Studio 7: A Comprehensive Guide</title>
<genre>Computer</genre>
<price>49.95</price>
<publish_date>2001-04-16</publish_date>
<description>Microsoft Visual Studio 7 is explored in depth,
looking at how Visual Basic, Visual C++, C#, and ASP+ are
integrated into a comprehensive development
environment.</description>
</book>
</catalog>
Updated XML saved to 'updated_xml_sample.xml'.
#include <stdlib.h>
#include "xml/xml.h"
#include "fmt/fmt.h"
int main() {
XmlDocument* doc = xml_parse_string("<?xml version='1.0'?><catalog><book id='bk101'><title>XML Developer's Guide</title><author>John Doe</author></book></catalog>");
XmlNode* root = xml_get_root(doc);
XmlNode* book = xml_get_element(root, "book", NULL);
if (book) {
XmlNode* title = xml_get_element(book, "title", NULL);
if (title && title->tag_name) {
const char* title_text = xml_get_element_text(title);
if (title_text) {
fmt_printf("Title: '%s'\n", title_text);
free((void*)title_text);
}
}
XmlNode* author = xml_get_element(book, "author", NULL);
if (author && author->tag_name) {
const char* author_text = xml_get_element_text(author);
if (author_text) {
fmt_printf("Author: '%s'\n", author_text);
free((void*)author_text);
}
}
}
xml_deallocate_document(doc);
return 0;
}
Result
Title: 'XML Developer's Guide'
Author: 'John Doe'
#include "xml/xml.h"
#include "fmt/fmt.h"
int main() {
// create root element as `store`
XmlDocument* doc = xml_create_document("store");
XmlNode* product = xml_create_element(doc, "product");
xml_append_child(xml_get_root(doc), product);
xml_set_element_attribute(product, "id", "p1001");
xml_set_element_attribute(product, "category", "electronics");
// Create a nested 'name' element and set its text content
XmlNode* name = xml_create_element(doc, "name");
xml_set_element_text(name, "Laptop");
xml_append_child(product, name);
// Create a nested 'price' element and set its text content
XmlNode* price = xml_create_element(doc, "price");
xml_set_element_text(price, "1200.00");
xml_append_child(product, price);
xml_set_element_attribute(product, "discounted", "yes");
char* xml_string = xml_to_string(doc);
if (xml_string) {
fmt_printf("Generated XML:\n%s\n", xml_string);
free(xml_string);
}
if (!xml_save_to_file(doc, "store.xml")) {
fmt_fprintf(stderr, "Failed to save the XML document.\n");
}
else {
fmt_printf("XML saved to 'store.xml'.\n");
}
xml_deallocate_document(doc);
return 0;
}
Result:
Generated XML:
<store>
<product id="p1001" category="electronics" discounted="yes">
<name>Laptop</name>
<price>1200.00</price>
</product>
</store>
XML saved to 'store.xml'.
#include "xml/xml.h"
#include "fmt/fmt.h"
int main() {
const char* xml_data = "<?xml version='1.0'?><library><book id='b001'><title>Programming in C</title><author>John Doe</author></book></library>";
XmlDocument* doc = xml_parse_string(xml_data);
if (!doc) {
fmt_fprintf(stderr, "Failed to parse XML string.\n");
return 1;
}
XmlNode* root = xml_get_root(doc);
if (!root) {
fmt_fprintf(stderr, "Failed to get root element.\n");
xml_deallocate_document(doc);
return 1;
}
XmlNode* book = xml_find_element_by_tag(root, "book");
if (book) {
const char* id = xml_get_element_attribute(book, "id");
if (id) {
fmt_printf("Book ID: %s\n", id);
}
XmlNode* title = xml_find_element_by_tag(book, "title");
if (title) {
fmt_printf("Original Title: %s\n", xml_get_element_text(title));
// Modify the book title
xml_set_element_text(title, "Advanced C Programming");
}
XmlNode* author = xml_find_element_by_tag(book, "author");
if (author) {
fmt_printf("Author: %s\n", xml_get_element_text(author));
}
// Add a new 'year' element
XmlNode* year = xml_create_element(doc, "year");
xml_set_element_text(year, "2024");
xml_append_child(book, year);
}
char* updated_xml = xml_to_string(doc);
if (updated_xml) {
fmt_printf("Updated XML:\n%s\n", updated_xml);
free(updated_xml);
}
if (!xml_save_to_file(doc, "library_updated.xml")) {
fmt_fprintf(stderr, "Failed to save the updated XML file.\n");
}
else {
fmt_printf("Updated XML saved to 'library_updated.xml'.\n");
}
xml_deallocate_document(doc);
return 0;
}
Result:
Book ID: b001
Original Title: Programming in C
Author: John Doe
Updated XML:
<library>
<book id="b001">
<title>Advanced C Programming</title>
<author>John Doe</author>
<year>2024</year>
</book>
</library>
Updated XML saved to 'library_updated.xml'.
This project is open-source and available under [ISC License].