Hello, I need to parse XML documents and replace the namespace prefixes, and am
not getting quite the behavior I expect. I am able to update the element
prefixes, but I end up with an extra namespace definition in the root node. I
wasn't sure if it mattered whether the call to setAttributeNS was done before
or after the traversal of child nodes to update element prefixes, so I tried
making the call both before and after child traversal with no change in output.
I even tried explicitly removing the extra namespace definition that I didn't
want, but the attribute that I want to remove isn't returned when I call
getAttributeNode, despite appearing in the output XML.
Am I doing something incorrectly? The example code below traverses the
DOMDocument, changing the addr: prefix to ns1. The test document and test code
output are also provided below. I expected xmlns:addr to be replaced with
xmlns:ns1, but instead I get both.
Regards,
Lara
void traverseNode( DOMNode *node )
{
if ( !node )
{
std::cout << "traverseNode: NULL node" << std::endl;
return;
}
std::string prefix = "ns1";
char *name = XMLString::transcode(node->getNodeName());
std::cout << "Traversing node " << name << std::endl;
if (node->getPrefix() && node->getNodeType () == DOMNode::ELEMENT_NODE ||
node->getNodeType () == DOMNode::ATTRIBUTE_NODE)
{
std::cout << "Resetting prefix for " << name << " to " << prefix <<
std::endl;
XMLCh *newPrefix = TranscodeFromStr((XMLByte*)prefix.c_str(),
prefix.length(), "UTF-8").adopt();
node->setPrefix( newPrefix );
}
if (node->getNodeType () == DOMNode::ELEMENT_NODE)
{
XMLCh *nsName = TranscodeFromStr((XMLByte*)"xmlns:addr", 9,
"UTF-8").adopt();
DOMAttr *oldAttr = ((DOMElement*)node)->getAttributeNode( nsName );
if ( oldAttr )
{
std::string ns = std::string("xmlns:") + prefix;
std::cout << "Resetting namespace prefix for " << nsName << " to " <<
ns << std::endl;
XMLCh *newNs = TranscodeFromStr((XMLByte*)ns.c_str(), ns.length(),
"UTF-8").adopt();
((DOMElement*)node)->setAttributeNS( XMLUni::fgXMLNSURIName, newNs,
oldAttr->getValue() );
}
}
for ( DOMNode *child = node->getFirstChild ( ); child; child =
child->getNextSibling ( ) )
{
traverseNode( child );
}
}
int main ( int argc, char *argv[ ] )
{
DOMDocument* doc = NULL;
<snip, parse input XML>
if ( doc )
{
DOMNode* rootNode = (DOMNode*) doc->getDocumentElement( );
// Traverse nodes, updating prefixes
traverseNode( rootNode );
// Try to explicitly remove the leftover xmlns:addr attribute
DOMNode* updatedRootNode = (DOMNode*) doc->getDocumentElement( );
char *updatedName = XMLString::transcode(updatedRootNode->getNodeName());
XMLCh *nsName = TranscodeFromStr((XMLByte*)"xmlns:addr", 9,
"UTF-8").adopt();
DOMAttr *oldAttr = ((DOMElement*)updatedRootNode)->getAttributeNode( nsName
);
if ( oldAttr )
{
std::cout << "Removing xmlns:addr from " << updatedName << std::endl;
((DOMElement*)updatedRootNode)->removeAttributeNode( oldAttr );
}
else
{
std::cout << "xmlns:addr not an attribute of " << updatedName <<
std::endl;
}
DOMLSSerializer *theSerializer =
((DOMImplementationLS*)impl)->createLSSerializer();
DOMLSOutput *theOutputDesc =
((DOMImplementationLS*)impl)->createLSOutput();
XMLCh* gOutputEncoding = 0;
theOutputDesc->setEncoding(gOutputEncoding);
XMLFormatTarget *myFormTarget;
myFormTarget=new StdOutFormatTarget();
theOutputDesc->setByteStream(myFormTarget);
std::cout << "OUTPUT DOC:" << std::endl;
std::cout << "--------------------------------------" << std::endl;
// write to stdout
theSerializer->write(doc, theOutputDesc);
theSerializer->release();
theOutputDesc->release();
delete myFormTarget;
}
}
Test document address.xml:
<?xml version="1.0"?>
<addr:address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://namespaces.oreilly.com/xmlnut/address
address-schema.xsd<http://namespaces.oreilly.com/xmlnut/address%20address-schema.xsd>"
xmlns:addr="http://namespaces.oreilly.com/xmlnut/address">
<addr:fullName>
<addr:first>Scott</addr:first>
<addr:last>Means</addr:last>
</addr:fullName>
</addr:address>
Test app output:
PARSING address.xml
Traversing node addr:address
Resetting prefix for addr:address to ns1
Traversing node #text
Traversing node addr:fullName
Resetting prefix for addr:fullName to ns1
Traversing node #text
Traversing node addr:first
Resetting prefix for addr:first to ns1
Traversing node #text
Traversing node #text
Traversing node addr:last
Resetting prefix for addr:last to ns1
Traversing node #text
Traversing node #text
Traversing node #text
xmlns:addr not an attribute of ns1:address
OUTPUT DOC:
--------------------------------------
<?xml version="1.0" encoding="UTF-8" standalone="no" ?><ns1:address
xmlns:ns1="http://namespaces.oreilly.com/xmlnut/address"
xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://namespaces.oreilly.com/xmlnut/address
address-schema.xsd<http://namespaces.oreilly.com/xmlnut/address%20address-schema.xsd>">
<ns1:fullName>
<ns1:first>Scott</ns1:first>
<ns1:last>Means</ns1:last>
</ns1:fullName>
</ns1:address>