Add a node to an existing XML document - c++

I am working on a small programm written in C++ using the QT Framework (v. 4.7).
The task I want to accomplish is opening an existing xml-document, add a node within its structure and save the document to the file I read it from before. I have tried this using something like that:
QFile xmlIn(AFileName);
QDomDocument doc("report_1");
if (xmlIn.open(QIODevice::ReadOnly)) {
if (doc.setContent(&xmlIn)) {
QDomElement docElem = doc.documentElement();
QDomNode n = docElem.firstChild();
// Do something with the element here
n = n.nextSibling();
}
}
// Code for saving the edited QDomDocument comes here
But I have the strong feeling that I am overseeing an essential part here, because this seems to be a task that is very common and normally the QT Lib should provide everything one could want :D
Please enlighten me :)

Use a "QXmlStreamWriter"...
QXmlStreamWriter lStreamWriter;
if(lProjectFile.open(QIODevice::WriteOnly|QFile::Text))
{
lStreamWriter.setDevice(&lProjectFile);
lStreamWriter.writeStartDocument();
lStreamWriter.writeStartElement("element");
lStreamWriter.writeAttribute("attribute","value");
lStreamWriter.writeEndElement();
lStreamWriter.writeEndDocument();
lProjectFile.close();
}

Related

Is it possible to Serialize QSettings and then recover them?

I would like to pass the settings saved with QSetting through two instances of an application, for example through a socket.
I could not see any function in the official documentation. The only thing I read is for example this post
Save Configuration Settings to XML file in QT?
But I do not want to save the settings in a XML file, for example in Windows I want to continue using the Registry.
I just want to collect all the settings, and pass them through a socket. And the receiver could check the settings and eventually substitute its own settings with the received ones.
Well, I suppose I could do something similar using QSettings::allKeys(), checking all the values, convert to strings, etc etc...but do you know if there is some native function in Qt already implemented?
Thanks to everyone in advance
Best solution that I found:
Create a QMap from QSettings
QMap<QString, QVariant> keysValuesPairs;
QStringList keys = settings.allKeys();
QStringListIterator it(keys);
while ( it.hasNext() )
{
QString currentKey = it.next();
keysValuesPairs.insert(currentKey, settings.value(currentKey));
}
And then write it in a QJson with the function (see the official documentation http://doc.qt.io/qt-5/qjsonobject.html)
QJsonObject::fromVariantMap
then in the other side recover it with
QJsonObject::toVariantMap()
and rewrite the settings
for ( int i = 0; i < keys.size(); i++ )
{
settings.setValue( keys.at(i), keysValuesPairsMap.value(keys.at(i)) );
}

Possible to get QDomElement from QXmlStreamWriter?

I'm writing a small XMPP server using qxmpp. Now I want to create a QXmppStanza and present it (as if a client had sent it) to the server and my plugins using
void QXmppServer::handleElement(const QDomElement &element)
This function requires a QDomElement and not a QXmppStanza. The only XML realted function I found in QXmppStanza and its derived classes (besides parse(...) ) is the function
void toXml(QXmlStreamWriter *writer)
I don't have experience with XML handling in qt yet, so is there a more performant way than writing the XML to a string/ByteArray, use it as input to create a new QDomElement and return its documentElement?
After doing some further research I have to accept it is not possible.
As stated in QDomDocument's documentation I always require a QDomDocument in order to work with a QDomElement (and other nodes):
Since elements, text nodes, comments, processing instructions, etc., cannot exist outside the context of a document (...)
The QXmlStreamWriter doesn't have a QDomDocument, so I really have to create a QDomDocument (which of course must live as long I want to work with the element) and then parse the text (QDomDocument::setContent).
I had a similar issue and was able to convert from a stream to a DOM element by doing something similar to what is shown below.
The first step is to stream to a byte array.
QByteArray data;
QXmlStreamWriter writer(&data);
object->toXml(&writer);
The second step is to set the content of a DOM document. The document's document element should be the DOM element you need.
QDomDocument temp;
if(temp.setContent(data))
QDomElement element = temp.documentElement(); // do whatever you want with this element

Xerces, xpaths, and XML namespaces

I'm trying to use xerces-c in order to parse a rather massive XML document generated from StarUML in order to change some things, but I'm running into issues getting the xpath query to work because it keeps crashing.
To simplify things I split out part of the file into a smaller XML file for testing, which looks like this:
<?xml version="1.0" encoding="utf-8"?>
<XPD:UNIT xmlns:XPD="http://www.staruml.com" version="1">
<XPD:HEADER>
<XPD:SUBUNITS>
</XPD:SUBUNITS>
</XPD:HEADER>
<XPD:BODY>
<XPD:OBJ name="Attributes[3]" type="UMLAttribute" guid="onMjrHQ0rUaSkyFAWtLzKwAA">
<XPD:ATTR name="StereotypeName" type="string">ConditionInteraction</XPD:ATTR>
</XPD:OBJ>
</XPD:BODY>
</XPD:UNIT>
All I'm trying to do for this example is to find all of the XPD:OBJ elements, of which there is only one. The problem seems to stem from trying to query with the namespace. When I pass a very simple xpath query of XPD:OBJ it will crash, but if I pass just OBJ it won't crash but it won't find the XPD:OBJ element.
I assume there's some important property or setting that I'm missing during initialization that I need to set but I have no idea what it might be. I looked up all of the properties of the parser having to do with namespace and enabled the ones I could but it didn't help at all so I'm completely stuck. The initialization code looks something like this, with lots of things removed obviously:
const tXercesXMLCh tXMLManager::kDOMImplementationFeatures[] =
{
static_cast<tXercesXMLCh>('L'),
static_cast<tXercesXMLCh>('S'),
static_cast<tXercesXMLCh>('\0')
};
// Instantiate the DOM parser.
fImplementation = static_cast<tXercesDOMImplementationLS *>(tXercesDOMImplementationRegistry::getDOMImplementation(kDOMImplementationFeatures));
if (fImplementation != nullptr)
{
fParser = fImplementation->createLSParser(tXercesDOMImplementationLS::MODE_SYNCHRONOUS, nullptr);
fConfig = fParser->getDomConfig();
// Let the validation process do its datatype normalization that is defined in the used schema language.
//fConfig->setParameter(tXercesXMLUni::fgDOMDatatypeNormalization, true);
// Ignore comments and whitespace so we don't get extra nodes to process that just waste time.
fConfig->setParameter(tXercesXMLUni::fgDOMComments, false);
fConfig->setParameter(tXercesXMLUni::fgDOMElementContentWhitespace, false);
// Setup some properties that look like they might be required to get namespaces to work but doesn't seem to help at all.
fConfig->setParameter(tXercesXMLUni::fgXercesUseCachedGrammarInParse, true);
fConfig->setParameter(tXercesXMLUni::fgDOMNamespaces, true);
fConfig->setParameter(tXercesXMLUni::fgDOMNamespaceDeclarations, true);
// Install our custom error handler.
fConfig->setParameter(tXercesXMLUni::fgDOMErrorHandler, &fErrorHandler);
}
Then later on I parse the document, find the root node, and then run the xpath query to find the node I want. I'll leave out the bulk of that and just show you where I'm running the xpath query in case there's something obviously wrong there:
tXercesDOMDocument * doc; // Comes from parsing the file.
tXercesDOMNode * contextNode; // This is the root node retrieved from the document.
tXercesDOMXPathResult * xPathResult;
doc->evaluate("XPD:OBJ", contextNode, nullptr, tXercesDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE), xPathResult);
The call to evaluate() is where it crashes somewhere deep inside xerces that I can't see very clearly, but from what I can see there are a lot of things that look deleted or uninitialized so I'm not sure what's causing the crash exactly.
So is there anything here that looks obviously wrong or missing that is required to make xerces work with XML namespaces?
The solution was right in front of my face the whole time. The problem was that you need to create and pass a resolver to the evaluate() call or else it will not be able to figure out any of the namespaces and will throw an exception. The crash seems to be a bug in xerces since it's crashing on trying to throw the exception when it can't resolve the namespace. I had to debug deep into the xerces code to find it, which gave me the solution.
So to fix the problem I changed the call to evaluate() slightly to create a resolver with the root node and now it works perfectly:
tXercesDOMDocument * doc; // Comes from parsing the file.
tXercesDOMNode * contextNode; // This is the root node retrieved from the document.
tXercesDOMXPathResult * xPathResult;
// Create the resolver with the root node, which contains the namespace definition.
tXercesDOMXPathNSResolver * resolver(doc->createNSResolver(contextNode));
doc->evaluate("XPD:OBJ", contextNode, resolver, tXercesDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE), xPathResult);
// Make sure to release the resolver since anything created from a `create___()`
// function has to be manually released.
resolver->release();

How to add a sub-group to a boost property tree?

I am trying to understand the syntax for boost property tree, but it's not jumping out at me. I am using a boost property_tree to represent a simple json document. In one case, I want to construct such a document, so I have:
property_tree::wptree json;
json.put(_eventName.c_str(), L"");
And this works fine, but sometimes I want to include values underneath the event like:
{ "event_name": {
"time":"4pm",
"day":"saturday" } }
What I want to do is:
property_tree::wptree json;
auto subTree = json.put(_eventName.c_str(), L"");
for(auto val : values)
subTree.put(val->first.c_str(), val->second.c_str());
but this seems to throw an exception. I've attempted to get a handle on this through the online docs, but I'm still a little unsure. I'm thinking maybe the solution is to construct a new wptree with the sub values, and then add that tree to the json document, but that seems a bit counterintuitive to me. Any advice?

Qt QSettings try to create ini file but none created why?

Im trying to create ini file that will hold me the configuration data, I have singletone class
that setting the QSettings object like this :
... #DEFINE CONFIG_FILE_NAME "myconfig.ini"
m_pSettings = new QSettings(QDir::currentPath()+"/"+CONFIG_FILE_NAME,QSettings::IniFormat);
this is accourding the document, but when i look in my application dir, there is none myconfig.ini file created, what im doing wrong ?
I believe in order to force QSettings file to appear you would need to set at least one value in it and then call sync() method. See if an example below would work for you:
QSettings* settings = new QSettings(QDir::currentPath() + "/my_config_file.ini", QSettings::IniFormat);
settings->setValue("test", "value");
settings->sync();
hope this helps, regards
I dont think that "/"+CONFIG_FILE_NAME return the expected result. May be the cause of your problem..
Anyway operator +() is present in QString class so QDir::currentPath() + "/my_config_file.ini" must work fine.