Poco library for c++, declare namespace for custom element - c++

I want to create an XML document by building a DOM document from scratch, with syntax like:
AutoPtr<Document> doc = new Document;
AutoPtr<Element> root = doc->createElement("root");
doc->appendChild(root);
AutoPtr<Element> element1 = doc->createElementNS("http://ns1", "ns1:element1");
root->appendChild(element1);
AutoPtr<Element> element2 = doc->createElementNS("http://ns1", "ns1:element2");
root->appendChild(element2);
DOMWriter writer;
writer.setNewLine("\n");
writer.setOptions(XMLWriter::PRETTY_PRINT);
writer.writeNode(std::cout, doc);
But, when I write it, I get next result:
<root>
<ns1:element1 xmlns:ns1="http://ns1"/>
<ns1:element2 xmlns:ns1="http://ns1"/>
</root>
So namespace ns1 declared two times, and I want to declare it inside "root" element.
Is there way to get next representation:
<root xmlns:ns1="http://ns1"/>
<ns1:element1/>
<ns1:element2/>
</root>

Related

Add XML contained in string as XML nodes to existing pugixml tree

I have a configuration file saver/loader. In addition to the expected data, there is a <CustomData> node. When saving the node, we'd simply have a std::string _customData and add it to the node, like this:
pugi::xml_document doc;
pugi::xml_node config = doc.append_child("OurConfig");
// save custom data
pugi::xml_node customData = config.append_child("CustomData");
customData.append_child(pugi::node_pcdata).set_value(_customData);
Our _customData was base64 encoded XML. It is provided from another part of the application. It must be a string, since the other part of the application uses different programming language (C#). As you can imagine, that became annoying, because it wasn't human readable. First step to fix this was simply to get rid of base64 in the app that provides _customData. So now we have readable version, which looks like this:
<?xml version="1.0"?>
<OurConfig>
<CustomData><CfgRoot>
<SomeValue name="External setting for foo" value="Foo"/>
<SomeValue name="External setting for bar" value="Bar"/>
</CfgRoot></CustomData>
</OurConfig>
But it could probably improve if the custom data was directly appended to XML tree instead of as string value. How can I append XML string as XML and not as string to pugixml tree?
Ie. the output I'd like:
<?xml version="1.0"?>
<OurConfig>
<CustomData>
<CfgRoot>
<SomeValue name="External setting for foo" value="Foo"/>
<SomeValue name="External setting for bar" value="Bar"/>
</CfgRoot>
</CustomData>
</OurConfig>
In the docs, there are three methods listed. I used the first one, making a convenience function like this:
bool AppendXMLString(pugi::xml_node target, const std::string& srcString)
{
// parse XML string as document
pugi::xml_document doc;
if (!doc.load_buffer(srcString.c_str(), srcString.length()))
return false;
for (pugi::xml_node child = doc.first_child(); child; child = child.next_sibling())
target.append_copy(child);
return true;
}

Multiple XML namespaces in xerces

I need to create XML files which contain multiple namespaces.
I create the root element with a default namespace, and add another
namespace ("otherNS") with setAttribute().
The problem is, that when i insert an element (with createElement()) which is prefixed with "otherNS",
xerces adds an empty namespace attribute. when I use createElementNS() and explicitly state the otherNS URI, xerces adds the full URI attribute.
In my understanding of XML namespaces, both is wrong. (Also the examples in
http://www.w3schools.com/Xml/xml_namespaces.asp do not repeat the namespace attributes in each element).
This is an example output:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<company xmlns="http://default.namespace.org/NS" xmlns:otherNS="http://other.namespace.org/ONS">
<otherNS:product xmlns="">Xerces-C</otherNS:product>
<otherNS:category xmlns:otherNS="http://other.namespace.org/ONS" idea="great">XML Parsing Tools</otherNS:category>
<developedBy xmlns="">Apache Software Foundation</developedBy>
</company>
And this is the code:
DOMDocument* doc = impl->createDocument(
X("http://default.namespace.org/NS"),
X("company"),
0);
DOMElement* rootElem = doc->getDocumentElement();
rootElem->setAttribute(
X("xmlns:otherNS"),
X("http://other.namespace.org/ONS"));
DOMElement* prodElem = doc->createElement(X("otherNS:product"));
rootElem->appendChild(prodElem);
DOMText* prodDataVal = doc->createTextNode(X("Xerces-C"));
prodElem->appendChild(prodDataVal);
DOMElement* catElem = doc->createElementNS(
X("http://other.namespace.org/ONS"),
X("otherNS:category"));
rootElem->appendChild(catElem);
My questions are:
Is my usage of the Xerces API correct? Do I maybe have to add the second namespace differently, it seems that xerces does not recognize it.
Are there maybe any features of the DOMLSSerializer class, which change that behaviour, so far I have not found any.
I got the solution on the Xerces mailing list:
Replace:
rootElem->setAttribute(
X("xmlns:otherNS"),
X("http://other.namespace.org/ONS"));
with:
rootElem->setAttributeNS(X("http://www.w3.org/2000/xmlns/"),
X("xmlns:otherNS"),
X("http://other.namespace.org/ONS"));
The reason: the namespace definition itself must be located in the xmlns namespace, so the setAttributeNS() method has to be used.

XML Getting attibute value of a Node

I am using XML DOM API in C++ to parse an XML file. I can't find any method to get the attribute value in a node element.
For example, in the following xml
<test>
<fruit count="10">
...
...
</fruit>
<test>
I need to get the count string("10") using XML APIs. Can anybody help me with some code snippets.
Use DOM Parser API to get attribute value count.
Refer below sample code:
//code to perform some process for parsing the input file and get rootElement
DOMNodeList* fruitNodes= rootElement->getElementsByTagName(XMLString::transcode("fruit"));
DOMNode* node = fruitNodes->item(0);
DOMElement* fruitElement = dynamic_cast <xercesc::DOMElement*>(node);
const XMLCh* attrValue = fruitElement->getAttribute(XMLString::transcode("count"));
you can get value 10 from attrValue using: string(XMLString::transcode(attrValue))
Based on http://msdn.microsoft.com/en-us/library/windows/desktop/ms754523(v=vs.85).aspx
Try something like:
pXMLDomNodeList = pXMLDocElement->selectNodes("/test/fruit/#count");

How does one bind namespace prefixes when using QXmlQuery (Qt XQuery)?

I'm attempting to use QXmlQuery to execute an XQuery expression against a document with a declared default namespace.
For discussion:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://namespace.com/ns1">
<node1 attr1="hi"/>
</root>
Now, I have the following to open and query against the document:
QFile temp("my.xml");
temp.open(QIODevice::ReadOnly | QIODevice::Text);
QXmlQuery query;
query.setFocus(&temp);
QXmlResultItems items;
query.setQuery("/root");
query.evaluateTo(&items);
In running this, 'items' never has data in it, since the document is namespaced. Of course, if I remove the default namespace declaration, 'items' does have the correct data, but I don't have that luxury.
I've tried changing the query to: "/prefix:root", and Qt barks a warning like:
No namespace binding exists for the prefix prefix in prefix:root
So namespace binding does exist! But where? I see QXmlNamePool, but it has no mutator methods. I can create a QXmlName with the pool from the query ala:
QXmlName name(query.namePool(), "prefix", "http://namespace.com/ns1");
But it doesn't change anything. I'm at a loss, other toolkits I have used have simple methods to bind prefixes to namespace URIs.
I believe if you would change your query to
...
QXmlResultItems items;
query.setQuery("declare default element namespace \"http://namespace.com/ns1\"; /root");
...
it should return the data.
hope this helps, regards

Adding a stylesheet declaration in my xml using Xerces-C

I have an application in c++ using Xerces-C as main xml manipulation library.
I have my DOMDocument* and my parser and I want to set declarations.
I do the following:
parser->setValidationScheme(xercesc::XercesDOMParser::Val_Never);
parser->setDoSchema(false);
parser->setLoadExternalDTD(false);
I want to add:
<?xml-stylesheet type="text/xsl" href="my_xslt.xsl"?>
How can I do it?
You'll need to use the createProcessingInstruction on the DOMDocument
http://xerces.apache.org/xerces-c/apiDocs-3/classDOMDocument.html#ce898787ba20c00c85be63f28a358507
Once you've created it, append it to the DocumentElement.
Here is the code for doing this:
xercesc::DomDocument *doc;
// ... (initialize doc in some way)
auto root = doc->getDocumentElement();
auto stylesheet = doc->createProcessingInstruction
(X("xml-stylesheet"), X("type=\"text/xsl\" href=\"custom.xsl\""));
doc->insertBefore(stylesheet, root);
This way, the stylesheet information appears in the prolog of the document, which is the typical place for it. X() is some function that encodes a C-style string as a Xerces-compatible XMLCh-string.