2

This is code to print an xml tree in C. How to write this iteratively?

static void
print_element_names(xmlNode *a_node)
{
    xmlNode *cur_node = NULL;

    for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
        if (cur_node->type == XML_ELEMENT_NODE) {
            printf("node type: Element, name: %s\n", cur_node->name);
        }
        print_element_names(cur_node->children);
    }
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Rocket
  • 123
  • 8
  • 5
    Any recursive code can be turned into iterative code by adding a stack to handle the stack frames that you would normally get with the recursion. But you'll essentially have what you have now, albeit with more complexity. Is there a compelling reason to make this iterative? – Robert Harvey Jan 30 '21 at 15:45
  • Any **tail** recursive code can be turned into iterative code. – Matheus Rossi Saciotto Feb 01 '21 at 00:04

2 Answers2

2

You can take advantage of the parent node in the xmlNode structure used in libxml2:

typedef struct _xmlNode xmlNode;
struct _xmlNode {
    void *_private;              // application data
    xmlElementType type;         // type number, must be second !
    const xmlChar *name;         // the name of the node, or the entity
    struct _xmlNode *children;   // parent->childs link
    struct _xmlNode *last;       // last child link
    struct _xmlNode *parent;     // child->parent link
    struct _xmlNode *next;       // next sibling link
    struct _xmlNode *prev;       // previous sibling link
    struct _xmlDoc *doc;         // the containing document End of common p
    xmlNs *ns;                   // pointer to the associated namespace
    xmlChar *content;            // the content
    struct _xmlAttr *properties; // properties list
    xmlNs *nsDef;                // namespace definitions on this node
    void *psvi;                  // for type/PSVI informations
    unsigned short line;         // line number
    unsigned short extra;        // extra data for XPath/XSLT
};

Here is a non recursive version:

static void print_element_names(xmlNode *a_node) {
    if (a_node != NULL) {
        xmlNode *stop = a_node->parent;
        xmlNode *cur_node = a_node;
        int skip = 0;

        for (;;) {
            if (!skip) {
                if (cur_node->type == XML_ELEMENT_NODE) {
                    printf("node type: Element, name: %s\n", cur_node->name);
                }
                if (cur_node->children) {
                    cur_node = cur_node->children;
                    continue;
                }
            }
            if (cur_node->next) {
                cur_node = cur_node->next;
                skip = 0;
            } else {
                cur_node = cur_node->parent;
                if (cur_node == stop)
                    break;
                skip = 1;
            }
        }
    }
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • if I print cur_node->content it prints "(null)" for every node.. how do I solve this? It prints the content when I use the statement cur_node->children->content. ?? I think something is wrong. – Rocket Feb 02 '21 at 16:48
  • Can you please explain what's happening in the code. I am really confused! – Rocket Feb 02 '21 at 17:41
  • `cur_node->content` may be null if there is no content for a particular node. Does the above code behave the same as your recursive function? – chqrlie Feb 02 '21 at 21:53
  • suppose for element in the xml file, when I print cur_node->name it prints "name" and name has some contents, but when I do cur_node->contents it prints "null" and if I do cur_node->children->content then it prints the content of the name element. – Rocket Feb 02 '21 at 22:46
  • @Rocket: you should add a test `if (cur_node->contents != NULL) { printf("contents: %s\n", cur_node->contents); }` – chqrlie Feb 03 '21 at 07:21
1

In the context of libxml2, here's a general approach that handles preorder and postorder traversal. As @chqrlie noted, libxml2 stores a pointer to the parent node, so you can iterate a subtree using constant memory.

typedef void
(*callbackFn)(xmlNode *cur, void *data);

void
iterateSubtree(xmlNode *cur,
               callbackFn callbackPreorder,
               callbackFn callbackPostorder,
               void *data) {
    xmlNode *orig;

    if (!cur)
        return;

    orig = cur;
    do {
        callbackPreorder(cur, data);

        while (cur->children) {
            cur = cur->children;
            callbackPreorder(cur, data);
        }

        while (cur != orig) {
            // Storing a copy of next and parent allows the postorder
            // callback to append to or delete a node.
            xmlNode *next = cur->next;
            xmlNode *parent = cur->parent;

            callbackPostorder(cur, data);

            if (next) {
                cur = next;
                break;
            }
            cur = parent;
        }
    } while (cur != orig);

    callbackPostorder(cur, data);
}
nwellnhof
  • 32,319
  • 7
  • 89
  • 113
  • Interesting generalisation. To emulate the OP's function, `iterateSubtree()` should be called in a loop iterating along the 'next' chain of siblings of the `a_node` argument. – chqrlie Feb 02 '21 at 06:49