6

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>
Parul Garg
  • 63
  • 1
  • 5
  • 4
    You are adding the xml nodes as text, so far the behavior is correct... – rekire Oct 17 '12 at 05:22
  • yes i have my data which is containg tags also. but when they are converted to xml format those tags are changed but i dont want that. so is there any solution for that? – Parul Garg Oct 17 '12 at 05:26
  • Please give me some direction whether it is possible in libxml2? Its very important for me. please help. – Parul Garg Oct 17 '12 at 05:35
  • Parul, I improved my answer, which now should be satifying. Please edit the title of your question to "How to add a node constructed from string" if you also think it would better describe the problem. Thanks. – Jarekczek Oct 17 '12 at 18:04

4 Answers4

3

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);
}
Jarekczek
  • 7,456
  • 3
  • 46
  • 66
  • Hi thanks for the answer. But now the problem is that this is working fine as long as we have only one tag element in our text. but in my data i have 2 tag elements like parrkstreetkolkata and it is printing only street tag. Any solution for this? Thanks in advance. – Parul Garg Oct 17 '12 at 09:12
  • Sorry, @Paul. I should have copied the node before adding it to another document. Now the code is corrected and shows also how to add multiple nodes, using a workarounding wrapper element. – Jarekczek Oct 17 '12 at 15:12
  • Hi Jarekczek, Thanks for your answer. I also did it in the same way. The difference is that you are explicitly including the root node and my root node is included in the string itself... – Parul Garg Oct 18 '12 at 04:07
  • Hi, i am facing one more problem. If i include the function xmlBufNodeDump and xmlBufContent then it is showing the error: undefined reference to `xmlBufNodeDump' – Parul Garg Oct 18 '12 at 08:43
  • That's a separate question. I thought something's wrong with my code. I get the same error, but `xmlNodeDump` works. – Jarekczek Oct 18 '12 at 10:07
  • Ya it was the separate question. seems like they have not included those functions in the tree.h header file or any other header file. anyway xmlNodeDump worked for me. Thanks..!! – Parul Garg Oct 19 '12 at 07:00
  • Parul, I meant don't hesitate to submit a new question after doing some search and research. I think it's interesting why this adviced fun does not work. However it must be in header files, because it is rejected at linking, not at compiling. – Jarekczek Oct 19 '12 at 07:20
  • This solution has a memory leak. after `xmlNodePtr addedNode = xmlAddChildList(parent, newNode->children);`, no one knows about `newNode` so it will not be freed. I have edited the answer so that there would be no memory leak. – milaniez Mar 04 '17 at 02:12
3

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);
Nazar
  • 603
  • 5
  • 6
  • Thanks, this was useful. My solution just required me to add the children of the new node to not have the < a > element in the final XML. – Arno Duvenhage Mar 04 '14 at 14:05
2

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");
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • hi thanks for the reply.. but this is not solving my problem. please see the Jarekczek's solution. but that also is working for only one node. can you help me regarding this. thanks in advance. – Parul Garg Oct 17 '12 at 09:17
1

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.

Community
  • 1
  • 1
Arno Duvenhage
  • 1,910
  • 17
  • 36