Qt xml: update attribute value identiefied by ID - c++

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.

Related

Calling next multiple times in loop causes stack overflow in ue4 Lua machine

I've been working on lua machine json object system. But I've hit a snag with the json object merger(You can see the functions code used below). When I try to iterate through a table using a while loop and IterateTable function, and then ether call Next or a function containing Next it causes an infinite loop that leads to a stack overflow crash.
Loops through the map elements of a user data object
void UJsonMergetFunctionLibrary::MergeLuaJsonObject(UMyDynamicObject* Origional, UMyDynamicObject* ToMerge, bool Override)
{
//Get all user data object keys
for (TPair<FString, FLuaValue>& P: ToMerge->GetTableContent())
{
//Holds the value of current key
FLuaValue Hold;
if (Origional->hasField(P.Key))
{
if ((P.Value.Type == ELuaValueType::Table) || (P.Value.Type == ELuaValueType::UObject))
{
Hold = Origional->LuaGetField(P.Key);
MergeLuaJsonObject(Hold, P.Value, Override);
}
else if (Override)
{
Origional->LuaSetField(P.Key, P.Value);
}
}
else
{
Origional->LuaSetField(P.Key, P.Value);
}
}
}
Loops through the elements of ether a lua table or user data object
void UJsonMergetFunctionLibrary::MergeLuaJsonObject(FLuaValue& Origional, FLuaValue& ToMerge, bool Override)
{
if (Origional.Type != ToMerge.Type) {
if (Override)
{
Origional = ToMerge;
return;
}
else
{
return;
}
}
//Table
if ((Origional.Type == ELuaValueType::Table) || (ToMerge.Type == ELuaValueType::Table)) {
TPair<FLuaValue, FLuaValue> Pair;
int32 MaxDex = MaxTableIndex(Origional);
while (IterateTable(ToMerge, Pair)) {
if (Pair.Key.Type == ELuaValueType::Integer)
{
MaxDex += 1;
Origional.SetFieldByIndex(MaxDex, Pair.Value);
}
else if (HasTableField(Origional, Pair.Key.ToString()))
{
FLuaValue Hold = Origional.GetField(Pair.Key.ToString());
if ((Pair.Value.Type == ELuaValueType::Table) && (Hold.Type == ELuaValueType::Table))
{
//Hold = OrigionalObj->LuaGetField(P.Key);
MergeLuaJsonObject(Hold, Pair.Value, Override);
}
else if((Pair.Value.Type == ELuaValueType::UObject) && (Hold.Type == ELuaValueType::UObject))
{
MergeLuaJsonObject(Hold, Pair.Value, Override);
}
else if (Override) {
Origional.SetField(Pair.Key.ToString(), Pair.Value);
}
}
else
{
Origional.SetField(Pair.Key.ToString(), Pair.Value);
}
}
}
//Object
else if ((Origional.Type == ELuaValueType::UObject) || (ToMerge.Type == ELuaValueType::UObject)) {
UMyDynamicObject* ToMergeObj = Cast<UMyDynamicObject>(ULuaBlueprintFunctionLibrary::Conv_LuaValueToObject(ToMerge));
UMyDynamicObject* OrigionalObj = Cast<UMyDynamicObject>(ULuaBlueprintFunctionLibrary::Conv_LuaValueToObject(Origional));
TMap<FString, FLuaValue> Keys = ToMergeObj->GetTableContent();
for (TPair<FString, FLuaValue>& P: Keys)
{
FLuaValue Hold;
if (OrigionalObj->hasField(P.Key))
{
if ((P.Value.Type == ELuaValueType::Table) || (P.Value.Type == ELuaValueType::UObject))
{
Hold = OrigionalObj->LuaGetField(P.Key);
MergeLuaJsonObject(Hold, P.Value, Override);
}
else if (Override)
{
OrigionalObj->LuaSetField(P.Key, P.Value);
}
}
else
{
OrigionalObj->LuaSetField(P.Key, P.Value);
}
//OrigionalObj->LuaSetField(P.Key, P)
}
Origional = FLuaValue(OrigionalObj);
}
}
Iterates and retrieves the next key in a table
bool UJsonMergetFunctionLibrary::IterateTable(FLuaValue Table, TPair<FLuaValue, FLuaValue>& Pair)
{
if (Table.Type != ELuaValueType::Table)
return false;
ULuaState* L = Table.LuaState;
if (!L)
return false;
if (Pair.Key.IsNil())
{
L->FromLuaValue(Table);
L->PushNil(); // first key
}
if (L->Next(-2))
{
Pair.Key = L->ToLuaValue(-2);
Pair.Value = L->ToLuaValue(-1);
if (GEngine)
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, Pair.Key.ToString());
L->Pop(); // pop the value
return true;
}
else
{
L->Pop(); // pop the table
return false;
}
}
Checks if the table contains a key
bool UJsonMergetFunctionLibrary::HasTableField(FLuaValue Table, FString FieldName)
{
if (Table.Type != ELuaValueType::Table)
return false;
ULuaState* L = Table.LuaState;
if (!L)
return false;
L->FromLuaValue(Table);
L->PushNil(); // first key
while (L->Next(-2))
{
FLuaValue Key = L->ToLuaValue(-2);
if (Key.ToString() == FieldName)
{
L->Pop(); // pop the value
L->Pop(); // pop the table
return true;
}
else {
L->Pop(); // pop the value
}
}
L->Pop(); // pop the table
return false;
}
1.I have tried poping both the value and the key when calling next within the loop, and than reassigning the old key once the internal next call ends.
2.I have tried googling it and Wasn't able to find anything.
3.I added a limit to how many times the loop can execute, to test if a infinite loops is causing the crash.
4.I tried making a function in pure lua that loops through a table and calls next within it(Didn't work, but do note I'm new to lua).
None of what I've tried has worked. What I want to happen is that when both Original and ToMerge have the same key, and the value assigned to that key is table I want it to perform recursion and merge those two tables than once thats done return to the execution on the original loop. Any help would be appreciated.

QXMLStreamReader is skipping over text elements

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?

QXmlQuery change node found by XPath query

I have a html tree and want to alter some attributes e.g. style of all nodes found by an QXmlQuery.
My code looks like this:
QXmlQuery query;
query.setFocus(data);
query.setQuery(xpathSelector);
QXmlResultItems result;
if (query.isValid()) {
query.evaluateTo(&result);
QXmlItem item(result.next());
while (!item.isNull()) {
if (item.isNode()) {
// alter the node here <--
}
item = result.next();
}
if (result.hasError()) {
/* Runtime error! */
}
}
How can I change attributes of a node in QXmlResultItems?

tinyxml parsing xml file

I have a xml file like this:
<?xml version="1.0"?>
<ApplicationSettings>
<BeamGeometry
Dimension="2"
Type="fan"
Shape="arc"
LengthFocalPointToISOCenter="558"
LengthISOCenterToDetector="394"
LengthDetectorSeperation="0.98"
LengthModuleSeperation="0.04"
NumberModules="57"
NumberDetectorsPerModule="16"
NumberISOCenterShift="3.25" />
</ApplicationSettings>
And I'd like to use tinyxml retrieving all the values (such as 558) based on the entry name such as (LengthFocalPointToISOCenter). Here is my code, not successful yet.
int SetFanbeamGeometry(const char* filename)
{
int ret = TRUE;
TiXmlDocument doc("E:\\Projects\\iterativeRecon\\ProjectPackage\\ApplicationSettings\\ApplicationSettings.xml");
int LengthFocalPointToISOCenter;
if( doc.LoadFile())
{
TiXmlHandle hDoc(&doc);
TiXmlElement *pRoot, *pParm;
pRoot = doc.FirstChildElement("ApplicationSettings");
if(pRoot)
{
pParm = pRoot->FirstChildElement("BeamGeometry");
int i = 0; // for sorting the entries
while(pParm)
{
pParm = pParm->NextSiblingElement("BeamGeometry");
i++;
}
}
}
else
{
printf("Warning: ApplicationSettings is not loaded!");
ret = FALSE;
}
return ret;
}
I am wondering how can I use tinyxml to do that? Sorry I am a first time user. and it looks confusing to me. Thanks.
There's only one BeamGeometry child element in the snippet you've shown; the information you're trying to access are its attributes - they're not individual elements.
So you need something like this:
// ...
pParm = pRoot->FirstChildElement("BeamGeometry");
if (pParm)
{
const char* pAttr = pParm->Attribute("LengthFocalPointToISOCenter");
if (pAttr)
{
int iLengthFocalPointToISOCenter = strtoul(pAttr, NULL, 10);
// do something with the value
}
}
If you want to loop through all attributes, it's quite simple:
const TiXmlAttribute* pAttr = pParm->FirstAttribute();
while (pAttr)
{
const char* name = pAttr->Name(); // attribute name
const char* value = pAttr->Value(); // attribute value
// do something
pAttr = pAttr->Next();
}

How to make xml parser more clear

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