The fossil_media_xml library provides a lightweight DOM-style API for parsing, manipulating, and serializing XML data in C. Nodes support elements, text, comments, CDATA sections, and processing instructions. Attributes and child nodes are dynamically managed. The C++ wrapper adds RAII, move semantics, and convenient methods to create nodes, append children, manipulate attributes, and serialize XML safely.
Code reference for C and C++ APIs for the respective Fossil Logic library.
HEADER REFERENCE #
#ifndef FOSSIL_MEDIA_XML_H
#define FOSSIL_MEDIA_XML_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Node type in the XML DOM.
*/
typedef enum fossil_media_xml_type_t {
FOSSIL_MEDIA_XML_ELEMENT, /**< Standard XML element */
FOSSIL_MEDIA_XML_TEXT, /**< Text node */
FOSSIL_MEDIA_XML_COMMENT, /**< Comment node */
FOSSIL_MEDIA_XML_PI,
FOSSIL_MEDIA_XML_CDATA
} fossil_media_xml_type_t;
/**
* @brief Error codes for XML operations.
*/
typedef enum fossil_media_xml_error_t {
FOSSIL_MEDIA_XML_OK = 0,
FOSSIL_MEDIA_XML_ERR_MEMORY,
FOSSIL_MEDIA_XML_ERR_PARSE
} fossil_media_xml_error_t;
/**
* @brief XML DOM Node structure.
*
* Stores element names, attributes, text content, and hierarchical children.
* Children and attributes are stored in dynamically allocated arrays.
*/
typedef struct fossil_media_xml_node_t {
fossil_media_xml_type_t type; /**< Node type (element, text, comment) */
char *name; /**< Element name (NULL for text/comment nodes) */
char *content; /**< Text or comment content (NULL for elements without direct text) */
struct fossil_media_xml_node_t **children; /**< Array of pointers to child nodes */
size_t child_count; /**< Number of child nodes */
char **attr_names; /**< Array of attribute names */
char **attr_values; /**< Array of attribute values */
size_t attr_count; /**< Number of attributes */
struct fossil_media_xml_node_t *parent; /**< Parent node (NULL for root) */
} fossil_media_xml_node_t;
/* ----------------------------------------------------------------------
* Parsing and memory management
* ---------------------------------------------------------------------- */
/**
* Parse XML text into a DOM-like tree.
* - xml_text must be NUL-terminated.
* - The returned tree must be freed with fossil_media_xml_free().
* @param xml_text The XML string to parse.
* @param err_out Pointer to an error struct for reporting errors (may be NULL).
* @return Pointer to the root node of the parsed document, or NULL on failure.
*/
fossil_media_xml_node_t *
fossil_media_xml_parse(const char *xml_text, fossil_media_xml_error_t *err_out);
/**
* Free an XML DOM tree.
* @param node Pointer to the root node to free.
*/
void fossil_media_xml_free(fossil_media_xml_node_t *node);
/* ----------------------------------------------------------------------
* Node creation helpers
* ---------------------------------------------------------------------- */
/**
* Create a new XML element node.
* @param name The element name (copied internally).
* @return Pointer to new node, or NULL on allocation failure.
*/
fossil_media_xml_node_t *fossil_media_xml_new_element(const char *name);
/**
* Create a new text node.
* @param text The text content (copied internally).
*/
fossil_media_xml_node_t *fossil_media_xml_new_text(const char *text);
/**
* Create a new comment node.
* @param text The comment text (copied internally).
*/
fossil_media_xml_node_t *fossil_media_xml_new_comment(const char *text);
/**
* Create a new CDATA section node.
* @param text The CDATA content (copied internally).
*/
fossil_media_xml_node_t *fossil_media_xml_new_cdata(const char *text);
/**
* @brief Create a new processing instruction (PI) XML node.
*
* @param target The PI target (e.g., "xml-stylesheet").
* @param data The PI data string.
* @return Pointer to the new PI node, or NULL on allocation failure.
*/
fossil_media_xml_node_t *fossil_media_xml_new_pi(const char *target, const char *data);
/* ----------------------------------------------------------------------
* Tree manipulation
* ---------------------------------------------------------------------- */
/**
* Append a child node to a parent.
* @param parent Parent node (must be element or document).
* @param child Child node to append (ownership transferred).
* @return 0 on success, nonzero on error.
*/
int fossil_media_xml_append_child(fossil_media_xml_node_t *parent, fossil_media_xml_node_t *child);
/**
* Get the first child node of a parent.
* @param node Parent node.
* @return Pointer to first child, or NULL if none.
*/
fossil_media_xml_node_t *fossil_media_xml_first_child(fossil_media_xml_node_t *node);
/**
* Get the next sibling node.
* @param node Current node.
* @return Pointer to next sibling, or NULL if none.
*/
fossil_media_xml_node_t *fossil_media_xml_next_sibling(const fossil_media_xml_node_t *node);
/* ----------------------------------------------------------------------
* Attributes
* ---------------------------------------------------------------------- */
/**
* Set or replace an attribute on an element node.
* @param elem Element node.
* @param name Attribute name.
* @param value Attribute value.
* @return 0 on success, nonzero on error.
*/
int fossil_media_xml_set_attribute(fossil_media_xml_node_t *elem, const char *name, const char *value);
/**
* Get an attribute value by name.
* @param elem Element node.
* @param name Attribute name.
* @return Attribute value string, or NULL if not found.
*/
const char *fossil_media_xml_get_attribute(const fossil_media_xml_node_t *elem, const char *name);
/* ----------------------------------------------------------------------
* Serialization
* ---------------------------------------------------------------------- */
/**
* Serialize an XML node (and its children) to a string.
* - Caller must free returned string with free().
* @param node Node to serialize.
* @param pretty If nonzero, indent for human readability.
* @param err_out Optional error output.
* @return Heap-allocated XML string, or NULL on failure.
*/
char *fossil_media_xml_stringify(const fossil_media_xml_node_t *node, int pretty, fossil_media_xml_error_t *err_out);
/* ----------------------------------------------------------------------
* Utility
* ---------------------------------------------------------------------- */
/**
* Get the type name of an XML node type.
* @param t Node type enum value.
* @return String representation (static).
*/
const char *fossil_media_xml_type_name(fossil_media_xml_type_t t);
#ifdef __cplusplus
}
#include <string>
#include <stdexcept>
#include <utility>
namespace fossil {
namespace media {
/**
* @brief C++ RAII wrapper for fossil_media_xml_node_t and related XML operations.
*
* Provides safe memory management and convenient methods for XML parsing,
* node creation, tree manipulation, attribute handling, and serialization.
*/
class Xml {
public:
/**
* @brief Construct an empty Xml object (null node).
*/
Xml() noexcept : node_(nullptr) {}
/**
* @brief Parse XML text into a DOM-like tree.
* @param xml_text The XML string to parse.
* @throws std::runtime_error on parse error.
*/
explicit Xml(const char* xml_text) {
fossil_media_xml_error_t err = FOSSIL_MEDIA_XML_OK;
node_ = fossil_media_xml_parse(xml_text, &err);
if (!node_ || err != FOSSIL_MEDIA_XML_OK) {
throw std::runtime_error("XML parse error");
}
}
/**
* @brief Construct from an existing node (takes ownership).
* @param node Pointer to fossil_media_xml_node_t.
*/
explicit Xml(fossil_media_xml_node_t* node) noexcept : node_(node) {}
/**
* @brief Move constructor.
*/
Xml(Xml&& other) noexcept : node_(other.node_) {
other.node_ = nullptr;
}
/**
* @brief Move assignment.
*/
Xml& operator=(Xml&& other) noexcept {
if (this != &other) {
reset();
node_ = other.node_;
other.node_ = nullptr;
}
return *this;
}
/**
* @brief Destructor. Frees the XML DOM tree.
*/
~Xml() { reset(); }
/**
* @brief Release ownership of the node.
* @return Pointer to fossil_media_xml_node_t, or nullptr.
*/
fossil_media_xml_node_t* release() noexcept {
auto* tmp = node_;
node_ = nullptr;
return tmp;
}
/**
* @brief Reset to a new node, freeing the old one.
* @param node New node pointer (default nullptr).
*/
void reset(fossil_media_xml_node_t* node = nullptr) noexcept {
if (node_) fossil_media_xml_free(node_);
node_ = node;
}
/**
* @brief Access the underlying node pointer.
*/
fossil_media_xml_node_t* get() noexcept { return node_; }
const fossil_media_xml_node_t* get() const noexcept { return node_; }
/**
* @brief Create a new XML element node.
* @param name Element name.
* @return Xml object owning the new node.
*/
static Xml new_element(const char* name) {
return Xml(fossil_media_xml_new_element(name));
}
/**
* @brief Create a new text node.
* @param text Text content.
* @return Xml object owning the new node.
*/
static Xml new_text(const char* text) {
return Xml(fossil_media_xml_new_text(text));
}
/**
* @brief Create a new comment node.
* @param text Comment text.
* @return Xml object owning the new node.
*/
static Xml new_comment(const char* text) {
return Xml(fossil_media_xml_new_comment(text));
}
/**
* @brief Create a new CDATA section node.
* @param text CDATA content.
* @return Xml object owning the new node.
*/
static Xml new_cdata(const char* text) {
return Xml(fossil_media_xml_new_cdata(text));
}
/**
* @brief Create a new processing instruction node.
* @param target PI target.
* @param data PI data.
* @return Xml object owning the new node.
*/
static Xml new_pi(const char* target, const char* data) {
return Xml(fossil_media_xml_new_pi(target, data));
}
/**
* @brief Append a child node to this node.
* @param child Xml object (ownership transferred).
* @throws std::runtime_error on error.
*/
void append_child(Xml&& child) {
if (!node_ || !child.node_) throw std::runtime_error("Null node");
if (fossil_media_xml_append_child(node_, child.node_) != 0)
throw std::runtime_error("Failed to append child");
child.node_ = nullptr;
}
/**
* @brief Get the first child node.
* @return Xml object (non-owning, does not free).
*/
Xml first_child() const {
if (!node_) return Xml();
return Xml(fossil_media_xml_first_child(node_));
}
/**
* @brief Get the next sibling node.
* @return Xml object (non-owning, does not free).
*/
Xml next_sibling() const {
if (!node_) return Xml();
return Xml(const_cast<fossil_media_xml_node_t*>(
fossil_media_xml_next_sibling(node_)));
}
/**
* @brief Set or replace an attribute on an element node.
* @param name Attribute name.
* @param value Attribute value.
* @throws std::runtime_error on error.
*/
void set_attribute(const char* name, const char* value) {
if (!node_) throw std::runtime_error("Null node");
if (fossil_media_xml_set_attribute(node_, name, value) != 0)
throw std::runtime_error("Failed to set attribute");
}
/**
* @brief Get an attribute value by name.
* @param name Attribute name.
* @return Attribute value string, or nullptr if not found.
*/
const char* get_attribute(const char* name) const {
if (!node_) return nullptr;
return fossil_media_xml_get_attribute(node_, name);
}
/**
* @brief Serialize this node (and its children) to a string.
* @param pretty Indent for human readability.
* @return std::string with XML content.
* @throws std::runtime_error on error.
*/
std::string stringify(bool pretty = false) const {
if (!node_) return {};
fossil_media_xml_error_t err = FOSSIL_MEDIA_XML_OK;
char* str = fossil_media_xml_stringify(node_, pretty ? 1 : 0, &err);
if (!str || err != FOSSIL_MEDIA_XML_OK)
throw std::runtime_error("XML stringify error");
std::string result(str);
free(str);
return result;
}
/**
* @brief Get the type name of this node.
* @return Static string representation.
*/
const char* type_name() const {
if (!node_) return nullptr;
return fossil_media_xml_type_name(node_->type);
}
// Non-copyable
Xml(const Xml&) = delete;
Xml& operator=(const Xml&) = delete;
private:
fossil_media_xml_node_t* node_;
};
} // namespace media
} // namespace fossil
#endif
#endif /* FOSSIL_MEDIA_XML_H */SAMPLE CODE C #
#include "fossil/media/xml.h"
#include <stdio.h>
int main() {
const char *xml_text =
"<root>"
"<item id='1'>Hello</item>"
"<item id='2'>World</item>"
"</root>";
fossil_media_xml_error_t err;
fossil_media_xml_node_t *root = fossil_media_xml_parse(xml_text, &err);
if (!root) {
printf("Failed to parse XML\n");
return 1;
}
fossil_media_xml_node_t *child = fossil_media_xml_first_child(root);
while (child) {
const char *id = fossil_media_xml_get_attribute(child, "id");
printf("Item id=%s, content=%s\n", id ? id : "(none)", child->content ? child->content : "");
child = fossil_media_xml_next_sibling(child);
}
fossil_media_xml_free(root);
return 0;
}
SAMPLE CODE C++ #
#include "fossil/media/xml.h"
#include <iostream>
int main() {
try {
const char* xml_text =
"<root>"
"<item id='1'>Hello</item>"
"<item id='2'>World</item>"
"</root>";
fossil::media::Xml doc(xml_text);
for (auto child = doc.first_child(); child.get() != nullptr; child = child.next_sibling()) {
const char* id = child.get_attribute("id");
std::cout << "Item id=" << (id ? id : "(none)")
<< ", content=" << (child.get()->content ? child.get()->content : "")
<< "\n";
}
std::cout << "Serialized XML:\n" << doc.stringify(true) << "\n";
} catch (const std::runtime_error& e) {
std::cerr << "XML error: " << e.what() << "\n";
}
}