I am writing out an XML file with some data. Later I want to read the XML file and use the data in my program. However, when I read it back in, QXMLStreamReader seems to be skipping over some child elements.
Here is a snip from my XML file:
<?xml version="1.0" encoding="UTF-8"?>
<TEMPROOT>
<Parent1>
<C0>238.195|1401.12</C0>
<C1>795.475|1087.65</C1>
<C2>995.748|756.766</C2>
</Parent1>
<Parent2>
<left>248</left>
<right>671</right>
<Width>496</Width>
<Height>583</Height>
</Parent2>
<Parent3>
<Number>9</Number>
<Blue>4</Blue>
<Red>5</Red>
</Parent3>
</TEMPROOT>
It reads Parent1 and its children correctly. My code recognizes Parent2, but skips over its children and goes directly for Parent3 where it reads its children correctly.
Here is my code where I WRITE out the XML:
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("TEMPROOT");
xmlWriter.writeStartElement("Parent1");
xmlWriter.writeTextElement("C0", str0);
xmlWriter.writeTextElement("C1", str1);
xmlWriter.writeTextElement("C2", str2);
xmlWriter.writeEndElement();
xmlWriter.writeStartElement("Parent2");
xmlWriter.writeTextElement("left", QString::number(rectCoord[0]));
xmlWriter.writeTextElement("right", QString::number(rectCoord[1]));
xmlWriter.writeTextElement("Width", QString::number(rectCoord[2]));
xmlWriter.writeTextElement("Height", QString::number(rectCoord[3]));
xmlWriter.writeEndElement();
xmlWriter.writeStartElement("Parent3");
xmlWriter.writeTextElement("Number", strNum);
xmlWriter.writeTextElement("Blue", strBlue);
xmlWriter.writeTextElement("Red", strRed);
xmlWriter.writeEndElement();
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
Here is how I READ in the XML file:
QXmlStreamReader xmlReader(&file);
QVector<QString> Qv1;
QVector<double> QV2;
QVector<QString> Qv3;
while (!xmlReader.isEndDocument())
{
xmlReader.readNext();
QString Name = xmlReader.name().toString();
if (Name == "Parent1")
{
while (xmlReader.name().toString() != "Parent2")
{
if (xmlReader.name().toString().at(0) == "C")
{
QV1.append(xmlReader.readElementText());
}
xmlReader.readNext();
}
}
else if (Name == "Parent2")
{
while (xmlReader.name().toString() != "Parent3")
{
if (xmlReader.name().toString() == "left")
{
QV2.append(xmlReader.readElementText().toDouble());
}
else if (xmlReader.name().toString() == "right")
{
QV2.append(xmlReader.readElementText().toDouble());
}
else if (xmlReader.name().toString() == "Width")
{
QV2.append(xmlReader.readElementText().toDouble());
}
else if (xmlReader.name().toString() == "Height")
{
QV2.append(xmlReader.readElementText().toDouble());
}
xmlReader.readNext();
}
}
else if (Name == "Number")
{
Qv3.append(xmlReader.readElementText());
}
else if (Name == "Blue")
{
Qv3.append(xmlReader.readElementText());
}
else if (Name == "Red")
{
Qv3.append(xmlReader.readElementText());
}
}
The MAIN issue is that the Name variable, which is the name of the current XML element on xmlReader goes from Parent2 straight to Parent3 ignoring the children of Parent2. The children of Parent1 and Parent3 are being read in with no problems. Am I doing something wrong when I read or write the xml?
Related
i'm just trying to parse an xml file using QtXml lib , so i need some help b'cause i don't understand how it's work :$
so this is my xml file config.xml :
<configuration>
<user>
<foo>Something</foo>
<bar>Something too</bar>
</user>
<dll_version>
<v1>appv1</v1>
<v2>appv2</v2>
<v3>appv3</v3>
</dll_version>
</configuration>
note : dll version it's a function that get version of an app from reg and compare it to value in xml file .
what i want to do :
Create function like : GetConfValue(QString conftype, QString confname)
that return the Config Value example : GetConfValue("user", "foo") return "Something"
thanks :)
Once in past, I also needed similar functionality. I used code as below. This may not be best solution, but i worked for me.
QString GetConfValue(QString conftype, QString confname) {
QString ret = "";
QFile file("config.xml");
if (file.open(QFile::ReadOnly | QFile::Text)) {
QXmlStreamReader reader;
reader.setDevice(&file); //(file);
reader.readNext();
while (!reader.atEnd()) {
if (reader.name() == "configuration" && reader.isStartElement()) {
reader.readNext();
while (!reader.atEnd()) {
if (reader.name() == conftype && reader.isStartElement()) {
reader.readNext();
while (!reader.atEnd()) {
if (reader.name() == confname && reader.isStartElement())
return reader.readElementText();
reader.readNext();
}
}
reader.readNext();
}
}
reader.readNext();
}
} else
qDebug() << qPrintable(file.errorString());
return ret;
}
I would like to update a single attribute value of the following xml file:
<table>
<obj ID="101103" name="name_a" type="nametype" cat="txt"/>
<obj ID="101104" name="name_b" type="nametype" cat="txt"/>
<obj ID="101105" name="name_c" type="nametype" cat="txt"/>
<obj ID="101106" name="name_d" type="nametype" cat="txt"/>
[...]
</table>
the code identifies the attribute to update by the "ID"-value. The ID comes first, the attribute (e.g. "name") follows.
static void xmlActions::writeXMLValue(QString XMLID, QString attrName, QString attrVal, QFile *XMLFile, bool newID)
{
if(XMLFile->open(QIODevice::ReadOnly))
{
//comes true if the ID is found:
bool hold = false;
if (XMLFile->open(QIODevice::ReadWrite))
{
QXmlStreamReader reader(XMLFile->readAll());
while(!reader.atEnd())
{
reader.readNext();
foreach(const QXmlStreamAttribute &attr, reader.attributes())
{
if (attr.value().toString() == XMLID)
{
// the ID has been found, flag is set
hold = true;
}
if (attr.name().toString() == attrName)
{
// now we are searching for the attribute which value is to change. If it 'belongs' to the found ID (hold == true) we change it's value:
if (hold == true)
{
//do changes to the attribute value, when it's found.
//e.g. change "name_a" to "name_x"
}
}
}
//reset the flag.
hold = false;
}
}
XMLFile->close();
}
}
My question is, how can I update that single value within this element.
I have an xml file built in this way:
<?xml version="1.0" encoding="UTF-8"?>
<Draw>
<Input>
<Cells>
<width>100</width>
<height>28</height>
</Cells>
<Column>custom</Column>
<Custom>
<header id="0">one</header>
<header id="1">two</header>
<header id="2">three</header>
<header id="3">four</header>
<header id="4">five</header>
</Custom>
</Input>
<Output>
<Cells>
<width>82</width>
<height>20</height>
</Cells>
<Column>upper</Column>
<Custom>
<header id="0">alfa</header>
<header id="1">beta</header>
<header id="2">gamma</header>
<header id="3">delta</header>
<header id="4">epsilon</header>
</Custom>
</Output>
</Draw>
And I’m trying to extrapolate the values of the header tag: since we have two sets of header tags (Input and Output) the only working code that I managed to work for now is this:
void MainWindow::readXmlFile() {
QString target;
QFile* file = new QFile("System/Settings.xml");
/* If we can't open it, let's show an error message. */
if (!file->open(QIODevice::ReadOnly | QIODevice::Text)) return;
QXmlStreamReader xmlReader(file);
/* We'll parse the XML until we reach end of it.*/
while(!xmlReader.atEnd() && !xmlReader.hasError()) {
QXmlStreamReader::TokenType token = xmlReader.readNext();
if(token == QXmlStreamReader::StartDocument) {
continue;
}
/* If token is StartElement, we'll see if we can read it.*/
if (token == 4) {
if (xmlReader.name() == "Input" || xmlReader.name() == "Output") {
target = xmlReader.name().toString();
while (!(xmlReader.tokenType() == QXmlStreamReader::EndElement && xmlReader.name() == target)) {
if (xmlReader.tokenType() == QXmlStreamReader::StartElement) {
qDebug() << xmlReader.name().toString();
if (xmlReader.name() == "width") {
QString num = xmlReader.readElementText();
//input->horizontalHeader()->setDefaultSectionSize(num.toInt());
}
if (xmlReader.name() == "height") {
QString num = xmlReader.readElementText();
//input->verticalHeader()->setDefaultSectionSize(num.toInt());
}
if (xmlReader.name() == "header") {
//headers->append(xmlReader.readElementText());
//qDebug() << xmlReader.readElementText();
}
//input->setHorizontalHeaderLabels(header);
}
xmlReader.readNext();
}
}
}
}
/* Error handling. */
if(xmlReader.hasError()) {
QMessageBox::critical(this,
"QXSRExample::parseXML",
xmlReader.errorString(),
QMessageBox::Ok);
}
xmlReader.clear();
}
Since this code seems very repetitive, especially from line 15 to 18, could you help me to make it a little cleaner? The examples in the web are not very explanatory.
Thanks in advance
I usually use lambda expressions. You will need to add CONFIG += c++11 to your .pro file.
Then define utilities for most repetitive patterns: for instance
/* If token is StartElement, we'll see if we can read it.*/
if (token == 4) {
auto name = [&]() { return xmlReader.name().toString(); };
auto type = [&]() { return xmlReader.tokenType(); };
auto num = [&]() { return xmlReader.readElementText().toInt(); };
if (name() == "Input" || name() == "Output") {
target = name();
while (!(type() == QXmlStreamReader::EndElement && name() == target)) {
if (type() == QXmlStreamReader::StartElement) {
qDebug() << name();
if (name() == "width") {
input->horizontalHeader()->setDefaultSectionSize(num());
}
else if (name() == "height") {
input->verticalHeader()->setDefaultSectionSize(num());
}
else if (name() == "header") {
//headers->append(xmlReader.readElementText());
//qDebug() << xmlReader.readElementText();
}
//input->setHorizontalHeaderLabels(header);
}
xmlReader.readNext();
}
}
unrelated warning: your code is leaking memory, instead of allocating with new, consider the simpler stack allocation:
QFile file("System/Settings.xml");
/* If we can't open it, let's show an error message. */
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return;
QXmlStreamReader xmlReader(&file);
I encountered the same problem few weeks ago, I had to read and write some xml file (arround 20/50 lines).
I started with QXmlStreamReader/Writer and finally give up and use QDomDocument.
The main difference (in terms of performance) between these two objects is QDomDocument loads all the xml file in memory. The syntax is also quite easier with QDomDoc !
See the doc for some written examples: http://qt-project.org/doc/qt-4.8/qdomdocument.html
I created this xml file with QXmlStreamWriter:
<?xml version="1.0" encoding="UTF-8"?>
<Draw>
<Input>
<Column title="A"/>
<Column title="B"/>
<Column title="C"/>
<Column title="D">
<item id="0">Bayer Leverkusen</item>
<item id="1">Benfica</item>
<item id="2">Villareal</item>
<item id="3">Montpellier</item>
</Column>
</Input>
</Draw>
I would like to create a Vector of String containing all the items inside the tag Column title="D": Now, I know how to create a QVector and how they fit elements on the inside, I just have to figure out how I can do this by extrapolating information from an xml file.
Can you help me?
You can use the QXmlStreamReader to iterate through the XML elements and find the <Column title="D"> element. Once you found it, the readNextStartElement() in combination of skipCurrentElement() can be used to read its all child elements.
Let's assume that the XML document you shown in your examle can be read from the xmlDocument object. To extract all <item> elements from <Column title="D"> element with appropriate error checking, you can do the following:
QXmlStreamReader xmlIterator(xmlDocument);
QVector<QString> output;
for(; !xmlIterator.atEnd(); xmlIterator.readNext()) {
if(isStartElementOfColumnD(xmlIterator)) {
while(xmlIterator.readNextStartElement()) {
if(isItemElement(xmlIterator))
output.append(xmlIterator.readElementText());
else
xmlIterator.skipCurrentElement();
}
}
}
if(xmlIterator.hasError())
qCritical() << "Error has occurred:" << xmlIterator.errorString();
else
qDebug() << output;
In the example above I used two predicates to hide the long and hardly readable validation of xmlIterator. These are the following:
inline bool isStartElementOfColumnD(const QXmlStreamReader& xmlIterator) {
return xmlIterator.isStartElement() && xmlIterator.name() == "Column" &&
xmlIterator.attributes().value("title") == "D";
}
inline bool isItemElement(const QXmlStreamReader& xmlIterator) {
return xmlIterator.name() == "item" &&
xmlIterator.attributes().hasAttribute("id");
}
Sample result:
QVector("Bayer Leverkusen", "Benfica", "Villareal", "Montpellier")
I would write it in the following way:
QVector<QString> store;
[..]
if (reader.readNextStartElement() && reader.name() == "Draw") {
while (reader.readNextStartElement() && reader.name() == "Input") {
while (reader.readNextStartElement()) {
QXmlStreamAttributes attr = reader.attributes();
if (reader.name() == "Column" && attr.value("title").toString() == "D") {
while(!(reader.isEndElement() && reader.name() == "Column")) {
if (reader.isStartElement() && reader.name() == "item") {
QString text = reader.readElementText();
store.append(text);
}
reader.readNext();
if (reader.hasError()) {
// Handle error.
QString msg = reader.errorString();
break;
}
}
} else {
reader.readNext();
}
}
}
} else {
reader.raiseError("Expected <Draw> element");
}
[..]
Trying to get Xerces-C to validate an XML file against a schema file but with no luck. The constructor below takes in the location of the XML file and the schema file and sets relevent member variables:
Config::Config(const std::string& schemaFile, const std::string& XMLFile)
: m_schemaFile(schemaFile),
m_XMLFile(XMLFile)
{
{
//initialize
try
{
xercesc::XMLPlatformUtils::Initialize();
xalanc::XPathEvaluator::initialize();
}
catch (xercesc::XMLException& e)
{
throw XercesInitialisationException();
}
}
{
//validate XML
xercesc::XercesDOMParser m_domParser;
if (NULL == m_domParser.loadGrammar(m_schemaFile.c_str(), xercesc::Grammar::SchemaGrammarType))
{
//schema file could not be loaded
throw SchemaLoadException();
}
ParserErrorHandler errorHandler;
m_domParser.setErrorHandler(&errorHandler);
m_domParser.setDoNamespaces(true);
m_domParser.setDoSchema(true);
m_domParser.setValidationConstraintFatal(true);
m_domParser.setValidationSchemaFullChecking(true);
m_domParser.parse(m_XMLFile.c_str());
if (NULL == m_domParser.getDocument() || NULL == m_domParser.getDocument()->getDocumentElement())
{
throw XMLLoadException();
}
if (0 == m_domParser.getErrorCount())
{
std::cout << "Number of schema validation errors: " << m_domParser.getErrorCount() << std::endl;
}
else
{
//m_validated unsuccessfully against the schema
throw SchemaValidationException();
}
}
{
//set up XPath interpreter
const xalanc::XalanDOMString m_xalanXMLFile(m_XMLFile.c_str());
const xercesc::LocalFileInputSource m_xalanInputSource(m_xalanXMLFile.c_str());
// Initialise XalanSourceTree subsystem...
xalanc::XalanSourceTreeInit sourceTreeInit;
m_liaison = std::auto_ptr<xalanc::XalanSourceTreeParserLiaison>(new xalanc::XalanSourceTreeParserLiaison(m_domSupport));
m_domSupport.setParserLiaison(m_liaison.get());
m_document = m_liaison->parseXMLStream(m_xalanInputSource);
m_prefixResolver = std::auto_ptr<xalanc::XalanDocumentPrefixResolver>(new xalanc::XalanDocumentPrefixResolver(m_document));
m_evaluator = std::auto_ptr<xalanc::XPathEvaluator>(new xalanc::XPathEvaluator);
}
}
The area of the constructor where the XML parse is set up is shown below. This is where I think the problem lies:
//validate XML
xercesc::XercesDOMParser m_domParser;
if (NULL == m_domParser.loadGrammar(m_schemaFile.c_str(), xercesc::Grammar::SchemaGrammarType))
{
//schema file could not be loaded
throw SchemaLoadException();
}
ParserErrorHandler errorHandler;
m_domParser.setErrorHandler(&errorHandler);
m_domParser.setDoNamespaces(true);
m_domParser.setDoSchema(true);
m_domParser.setValidationConstraintFatal(true);
m_domParser.setValidationSchemaFullChecking(true);
m_domParser.parse(m_XMLFile.c_str());
if (NULL == m_domParser.getDocument() || NULL == m_domParser.getDocument()->getDocumentElement())
{
throw XMLLoadException();
}
if (0 == m_domParser.getErrorCount())
{
std::cout << "Number of schema validation errors: " << m_domParser.getErrorCount() << std::endl;
}
else
{
//m_validated unsuccessfully against the schema
throw SchemaValidationException();
}
When the code is compiled and ran everything works but no validation is carried out on the XML against the schema. Which means the XML is parsed even if it does not conform to the schema. After checking I have also been able to ascertain that the errorCount value remains 0 for all schemas.
You must to reference the schema on xml root node like this:
<rootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="Schema.xsd">