Roman Theuer
Roman Theuer

Reputation: 25

libmxl2 - change value of element, with mixed content

I am doing some tiny wrapper for using libxml2 library. And there is problem with changing element value in case it has mixed content.

In following example, I am creating "child1" element with "OLD CONTENT" value and with sub-child "child1.1". Then I am trying to change "child1" value to "NEW CONTENT" using xmlNodeSetContent function (line marked with <---). The problem is, that "child1.1" is removed by xmlNodeSetContent. I want to change only the value from "OLD CONTENT" to "NEW CONTENT".

It seems that xmlNodeSetContent is not the proper function to do this or maybe there is something else in libxml2, which I am missing.

Question: is there a way how to change value in case of mixed content? If so, how to do that?

#include <stdio.h>
#include <string.h>

#include <libxml/parser.h>
#include <libxml/tree.h>

#define MY_ENCODING "ISO-8859-1"

xmlNodePtr createRoot(xmlDocPtr doc, char* name)
{
    xmlNodePtr root;
    root = xmlNewNode(NULL, BAD_CAST name);
    xmlDocSetRootElement(doc, root);
    return root;
}

xmlNodePtr addChild(xmlNodePtr parent, char* name, char* value)
{
    return xmlNewChild(parent, NULL, BAD_CAST name, BAD_CAST value);
}

void setElementContent(xmlNodePtr element, char* value)
{
    xmlNodeSetContent(element, BAD_CAST value);
}

void setElementAttribute(xmlNodePtr element, char* attribName, char* attribValue)
{
    xmlSetProp(element, BAD_CAST attribName, BAD_CAST attribValue);
}

int main(int argc, char** argv)
{
    xmlDocPtr doc = NULL;
    xmlNodePtr root = NULL;
    xmlNodePtr tmp1 = NULL;
    xmlNodePtr tmp11 = NULL;
    xmlNodePtr tmp2 = NULL;

    xmlChar* xmlbuff;
    int buffersize;


    doc = xmlNewDoc(BAD_CAST "1.0");

    root = createRoot(doc, "root");

    //tmp1 = addChild(root, "child1", NULL);
    tmp1 = addChild(root, "child1", "OLD CONTENT");

    tmp11 = addChild(tmp1, "child1.1", NULL);
    setElementContent(tmp11, "blablabla");

    tmp2 = addChild(root, "child2", NULL);
    setElementContent(tmp2, "22222");
    setElementAttribute(tmp2, "id", "002");
    setElementAttribute(tmp2, "lang", "en");

    setElementContent(tmp1, "NEW CONTENT");   // <---

    //xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1);
    xmlDocDumpFormatMemoryEnc(doc, &xmlbuff, &buffersize, MY_ENCODING, 1);
    printf("%s", (char *) xmlbuff);

    xmlCleanupParser();

    xmlFree(xmlbuff);
    xmlFreeDoc(doc);

    return 0;
}

EDIT: working version:

#include <stdio.h>
#include <string.h>

#include <libxml/parser.h>
#include <libxml/tree.h>

#define MY_ENCODING "ISO-8859-1"

xmlNodePtr createRoot(xmlDocPtr doc, char* name)
{
    xmlNodePtr root;
    root = xmlNewNode(NULL, BAD_CAST name);
    xmlDocSetRootElement(doc, root);
    return root;
}

xmlNodePtr addChild(xmlNodePtr parent, char* name, char* value)
{
    return xmlNewChild(parent, NULL, BAD_CAST name, BAD_CAST value);
}

void setElementContent(xmlNodePtr element, char* value)
{
    xmlNodePtr it = NULL;
    int bFound = 0;

    for (it = element->children; it != NULL; it = it->next)
    {
        if (it->type == XML_TEXT_NODE)
        {
            xmlNodeSetContent(it, BAD_CAST value);
            bFound = 1;
        }
    }

    if (!bFound)
    {
        xmlNodeSetContent(element, BAD_CAST value);
    }
}

void setElementAttribute(xmlNodePtr element, char* attribName, char* attribValue)
{
    xmlSetProp(element, BAD_CAST attribName, BAD_CAST attribValue);
}

void printXml(xmlDocPtr doc)
{
    xmlChar* xmlbuff;
    int buffersize;

    //xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1);
    xmlDocDumpFormatMemoryEnc(doc, &xmlbuff, &buffersize, MY_ENCODING, 1);
    printf("%s", (char *) xmlbuff);
    xmlCleanupParser();
    xmlFree(xmlbuff);
}

int main(int argc, char** argv)
{
    xmlDocPtr doc = NULL;
    xmlNodePtr root = NULL;
    xmlNodePtr tmp1 = NULL;
    xmlNodePtr tmp11 = NULL;
    xmlNodePtr tmp2 = NULL;
    xmlNodePtr tmp3 = NULL;
    xmlNodePtr it = NULL;

    doc = xmlNewDoc(BAD_CAST "1.0");

    root = createRoot(doc, "root");

    //tmp1 = addChild(root, "child1", NULL);
    tmp1 = addChild(root, "child1", "OLD CONTENT");

    tmp11 = addChild(tmp1, "child1.1", NULL);
    setElementContent(tmp11, "blablabla");

    tmp2 = addChild(root, "child2", NULL);
    setElementContent(tmp2, "22222");
    setElementAttribute(tmp2, "id", "002");
    setElementAttribute(tmp2, "lang", "en");

    tmp3 = addChild(root, "child3", NULL); /* create empty */

    printf("\nBefore:\n");
    printXml(doc);

    setElementContent(tmp1, "NEW CONTENT");
    setElementContent(tmp3, "333");

    printf("\nAfter:\n");
    printXml(doc);

    xmlFreeDoc(doc);

    return 0;
}

Upvotes: 2

Views: 1379

Answers (1)

Stephan Lechner
Stephan Lechner

Reputation: 35154

Mixed content is represented by a sequence of text nodes and element nodes. You have to retrieve the text node and replace just it's content:

xmlNodePtr textNode = &tmp1->children[0];
setElementContent(textNode, "NEW CONTENT");
//setElementContent(tmp1, "NEW CONTENT");   // <---

Upvotes: 1

Related Questions