Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a xml node constructed from string in libxml2

Tags:

c

xml

libxml2

I am using Libxml2 for encoding the data in a xml file. My data contain tags like "<" and ">". when it is converted into xml these tags are also converted into "&lt" and "&gt". Is there any way to solve this problem. I want to use those tags as xml nodes while decoding that xml file, so CDATA is not a solution for this problem. Please give any solution for this. Thanks.

Example Code:

xmlNewChild(node, NULL, (xmlChar *)"ADDRESS", (xmlChar *)"<street>Park Street</street><city>kolkata</city>");

and output of above code is:
<person>
<ADDRESS>&lt;street&gt;Park Street&lt;/street&gt;&lt;city&gt;Kolkata&lt;/city&gt;</ADDRESS>
like image 848
Parul Garg Avatar asked Oct 17 '12 05:10

Parul Garg


4 Answers

If you want a string to be treated as xml, then you should parse it and obtain xmlDoc from it, using xmlReadMemory. It could be usable for larger strings, but usually the document is builded using single step instructions, like in Joachim's answer. Here I present xmlAddChildFromString function to do the stuff in a string way.

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

/// Returns 0 on failure, 1 otherwise
int xmlAddChildFromString(xmlNodePtr parent, xmlChar *newNodeStr)
{
  int rv = 0;
  xmlChar *newNodeStrWrapped = calloc(strlen(newNodeStr) + 10, 1);
  if (!newNodeStrWrapped) return 0;
  strcat(newNodeStrWrapped, "<a>");
  strcat(newNodeStrWrapped, newNodeStr);
  strcat(newNodeStrWrapped, "</a>");
  xmlDocPtr newDoc = xmlReadMemory(
    newNodeStrWrapped, strlen(newNodeStrWrapped),
    NULL, NULL, 0);
  free(newNodeStrWrapped);
  if (!newDoc) return 0;
  xmlNodePtr newNode = xmlDocCopyNode(
    xmlDocGetRootElement(newDoc),
    parent->doc,
    1);
  xmlFreeDoc(newDoc);
  if (!newNode) return 0;
  xmlNodePtr addedNode = xmlAddChildList(parent, newNode->children);
  if (!addedNode) {
    xmlFreeNode(newNode);
    return 0;
  }
  newNode->children = NULL; // Thanks to milaniez
  newNode->last = NULL;     // for fixing
  xmlFreeNode(newNode);     // the memory leak.
  return 1;
}

int
main(int argc, char **argv)
{
    xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
    xmlNodePtr root = xmlNewNode(NULL, BAD_CAST "root");
    xmlDocSetRootElement(doc, root);
    xmlAddChildFromString(root,
      "<street>Park Street</street><city>kolkata</city>");
    xmlDocDump(stdout, doc);
    xmlFreeDoc(doc);
    return(0);
}
like image 170
Jarekczek Avatar answered Nov 15 '22 12:11

Jarekczek


You have to call xmlNewChild in a chain, one call for the parent node and a call each for each sub-node:

xmlNodePtr *addressNode = xmlNewChild(node, NULL, (xmlChar *) "address", NULL);
xmlNewChild(addressNode, NULL, (xmlChar *) "street", "Park Street");
xmlNewChild(addressNode, NULL, (xmlChar *) "city", "Koltaka");
like image 44
Some programmer dude Avatar answered Nov 15 '22 11:11

Some programmer dude


You can try to use function xmlParseInNodeContext. It allows you to parse raw XML in the context of parent node, and constructs a node that can be attached to the parent.

For example:

const char * xml = "<a><b><c>blah</c></b></a>";
xmlNodePtr new_node = NULL;

// we assume that 'parent' node is already defined
xmlParseInNodeContext(parent, xml, strlen(xml), 0, &new_node);
if (new_node) xmlAddChild(parent, new_node);
like image 27
Nazar Avatar answered Nov 15 '22 11:11

Nazar


I'm now using the following code to inject XML text (possibly containing multiple elements) into an existing node (thanks to Nazar and nwellnhof for the one answer and referring me from my question (Injecting a string into an XML node without content escaping) to this one):

std::string xml = "<a>" + str + "</a>";
xmlNodePtr pNewNode = nullptr;
xmlParseInNodeContext(pParentNode, xml.c_str(), (int)xml.length(), 0, &pNewNode);
if (pNewNode != nullptr)
{
    // add new xml node children to parent
    xmlNode *pChild = pNewNode->children;
    while (pChild != nullptr)
    {
        xmlAddChild(pParentNode, xmlCopyNode(pChild, 1));
        pChild = pChild->next;
    }

    xmlFreeNode(pNewNode);
}

It takes the string (str) adds a surrounding element (< a >...< a/ >), parses the string using xmlParseInNodeContext and then adds the children of the new node to the parent. It is important to add the children of the new node and not the new node to avoid having < a >...< a/ > in the final XML.

like image 1
Arno Duvenhage Avatar answered Nov 15 '22 12:11

Arno Duvenhage