skeleton18
skeleton18

Reputation: 11

Is there a way to automatically detect the namespaces written in the root tag using libxml2?

I am trying to write an XML parser using libxml2 (2.9.1 -> I know that it is old but I cannot update it right now). The XML test file I am using is confidential so I am not able to share it with you. But, within the file I have the following line:

<?xml version="1.0"?>
<log4j:test xmlns:log4j="http://jakarta.apache.org/log4j/">

My issue is about the log4j namespace. When using the xmlParseFile, I was assuming that it will be automatically detected but no. I have to explicitly call the function xmlXPathRegisterNs.

So my question is: is there a way to automatically register the namespaces read from the file? In the code below, I don't want to explicitly call xmlXPathRegisterNs.

Thank you for your help.

PS: Here is my beta-test code

int main() {                                                                                                                                                  
  std::string inputFilename = "myfile.xml";                                                                                                                    

  xmlDocPtr document = xmlParseFile(inputFilename.c_str());
  if (document == nullptr) {
    std::cerr << "Document not parsed successfully" << std::endl;
    return -1;                                                                                        
  }                                                                                                                                                         

  xmlXPathContextPtr context = xmlXPathNewContext(document);                                                                                                
  if (context == nullptr) {                                                                                                                                 
    std::cerr << "Error in xmlXPathNewContext" << std::endl;                                                                                              
    return -1;
  }                                                                                                                                                         

  if (xmlXPathRegisterNs(context,  BAD_CAST "log4j", BAD_CAST "http://jakarta.apache.org/log4j/") != 0) {                                                   
      std::cerr << "Unable to register namespace" << std::endl;
      return -1;                                                                                             
  }                                                                                                                                                         

  xmlChar *xpath = (xmlChar*) "//log4j:test/log4j:event";                                                                                                
  xmlXPathObjectPtr nodesList = xmlXPathEvalExpression(xpath, context);                                                                                     
  if (nodesList == nullptr) {                                                                                                                               
    std::cerr << "Error in xmlXPathEvalExpression" << std::endl;                                                                                         
    return -1;
  }                                                                                                                                                         

  if (xmlXPathNodeSetIsEmpty(nodesList->nodesetval)) {                                                                                                      
    std::cerr << "No result found for //log4j:test/log4j:event" << std::endl;
    return -1;                                                                                     
  } else {                                                                                                                                                  
    xmlNodeSetPtr nodeset = nodesList->nodesetval;                                                                                                        

    for (int i{0}; i < nodeset->nodeNr; i++) {                                                                                                            
      xmlChar *keyword = xmlNodeListGetString(document, nodeset->nodeTab[i]->xmlChildrenNode, 1);                                                       
      std::cout << "Keyword: " << nodeset->nodeTab[i]->name << std::endl;                                                                               

      xmlFree(keyword);                                                                                                                                 
    }                                                                                                                                                     

    xmlXPathFreeObject(nodesList);                                                                                                                        
  }                                                                                                                                                         

  xmlFreeDoc(document);                                                                                                                                     
  xmlCleanupParser();                                                                                                                                       

  return 0;                                                                                                                                                 
} 

Upvotes: 1

Views: 300

Answers (1)

Michael Kay
Michael Kay

Reputation: 163448

Actually, you're not trying to write an XML parser. libxml2 is an XML parser. You're trying to write an application that uses an XML parser.

When you write XPath expressions, you can bind any prefix you like to the namespaces of the elements you are looking for. You don't need to know what prefix is used in the source document.

If you don't want to register a namespace binding then your XPath expressions can't use any namespace prefixes and (given that this is XPath 1.0) you'll have to use the circumlocution //*[local-name()='test']/*[local-name()='event']

Upvotes: 2

Related Questions