Updating data on a XML DOM c++ using tinyxml-2 - c++

I was wondering how could I update the data on the DOM for a certain attribute? I've searched but I couldn't find anything. Basically, I have an attribute called Hour(for example it's "11:03") and I want the text from that specific attribute to be changed to something like "11:04" or any other different text.
if( strcmp(Code1,Code2) == 0 )
{
strcpy(New,NewHour);
Element->FindAttribute("Hour")->SetAttribute(New); // here I want it to be changed in the DOM but I dont know how to do it
}
Later edit: This is what I've tried, but it's telling me FindAttribute() is private..

It is true that you can use SetAttribute which accepts the attribute name and value as parameters.
However, TinyXml2 does have a methodology for using FindAttribute because I have this code in my application:
// We need to get the assistant
const XMLAttribute *pAttrAssistant = const_cast<const XMLElement*>(pStudent)->FindAttribute("Assistant");
if (pAttrAssistant != nullptr)
{
LPCTSTR szAssistant = CA2CT(pAttrAssistant->Value(), CP_UTF8);
SetStudentInfo(eSchool, eAssign, strStudent, szAssistant, iStudyPoint);
}
else
{
// TODO: Throw exception if Assistant attribute missing
}
As you can see, I use the FindAttribute method and I have no compilation errors. If you look closely you will see that I am using const and that is the key.
The class exposes two methods:
One of them is set to private as you have already found out. But the const overload is set as public:

Related

How to implement a property enumerator for a C++ native object in V8?

I'm writing a Node.js C++ module that communicates with a database which returns BSON objects, and wrap them in a V8 object so they can be accessed from the JavaScript.
For that I create an ObjectTemplate, and configure it through SetHandler. My getter and setter work, but the property enumerator does not return anything. Here is the code:
void PropertyEnumerator(const PropertyCallbackInfo<Array>& info)
{
// Enumerator, aka `Object.keys(obj)`
auto isolate = info.GetIsolate();
auto data = unrwap_internal_field<BsonObjectData>(info.Holder(), 0);
Local<Array> array = Array::New(isolate);
int i = 0;
bson_iter_t iter;
bson_iter_init_from_data(&iter, data->document_data, data->document_length);
while (bson_iter_next(&iter)) {
const char* key = bson_iter_key(&iter);
array->Set(i++, String::NewFromUtf8(isolate, key, v8::NewStringType::kNormal).ToLocalChecked());
}
info.GetReturnValue().Set(array);
}
I have checked that the function is actually called
I have checked that the array is populated with the right values
But in the JavaScript when I do console.log(Object.keys(obj)) I get an empty array. It's like info.GetReturnValue().Set(array); does not do anything. The documentation states that each element of the array must be a Name, and String inherits from Name so I really don't understand.
Thoughts?
As #jmrk suggested in their comment, the problem was that I didn't implement the query function for my object template. This made the enumerator work properly:
void PropertyQuery(Local<Name> property, const PropertyCallbackInfo<Integer>& info)
{
info.GetReturnValue().Set(PropertyAttribute::None);
}

QWebEngine: How to get attribute value?

I want to rewrite my project writen in QWebView to QWebEngineView, but I don't know how to get the attribute value and assign it to a C++ variable. I think that I should use QWebEnginePage::runJavaScript function. But I don't know how.
For example, when I try to display value of element in console, I did this:
web->page()->runJavaScript("document.getElementById(\"login\").getAttribute(\"va‌​lue\")", []
(QVariant result)->void {
qDebug()<<result.toString();
});
But It display nothing.
I believe you can get the value of your element by connecting a slot to the loadFinished signal of QWebView. The code should look something like below.
void WebView::onPageLoadFinished(bool status)
{
if(status)
{
QVariant value;
QWebFrame* frame = this->page()->currentFrame();
if (frame!=NULL)
{
QWebElement element = frame->findFirstElement("input[id=login]");
value = element.attribute("value");
}
//In your case you can set a class variable instead of local variable value
}
}
This method is clean and you can even add a url check right in the beginning of the SLOT and continue further only if the signal came from the url you are interested in.
EDIT: I just re-read your question and found that you want to implement it with QWebEngine. I am not sure if it can be done there. According to this page, you can't access the internal elements. I would love to know if there is a work around.
runJavaScript will definitely do it: Here is a functional example using pure Javascript to interact with a field named "email":
// This will set the value
this->_view->page()->runJavaScript(
"document.querySelector('input[name=\"email\"]').value = \"JKLJKLJKL\";"
);
// This will retrieve the value
this->_view->page()->runJavaScript(
"document.querySelector('input[name=\"email\"]').value;",
[](const QVariant &result){
qDebug() << "Value is: " << result.toString() << endl;
}
);
Remember that the scope of the QVariant is limited to the callback, if you need to use the returned value outside of the function, you will want to pass a variable that will survive until callback is called in the [].
There is the direct access to DOM attributes/properties in JS:
document.getElementById("login").va‌​lue
Also I would recommend to use jQuery (take a look at fancybrowser example). Code for set/get value of login input is:
if( qt.jQuery( 'input#login' ).length ) // can be omitted if you are sure that DOM element is ready
{
qt.jQuery( 'input#login' ).attr( 'value', currentUserName ); //-- set value
return qt.jQuery( 'input#login' ).attr( 'value' ); //-- get
}
This works fine.
_view->page()->runJavaScript("document.querySelector(input[name=\"email\"]).value");
Just add your callback as second parameter. I hope this helps you.
This works fine.
_view->page()->runJavaScript("document.querySelector(input[name=\"email\"]).value");
Just add your callback as second parameter to get the value. I hope this helps you.

Rapidjson assertions during HasMember

To all those that are familiar with rapidjson i have the following issue:
I have a certain function that accepts as parameter a date and if that date exists in the json file the method does some operations and if not some other operations.
Generally it looks like this: (not actual code more like pseudo)
Function:
void updateData(string date) {
//
//code to turn date from string to const char* (tested)
//
if (v.HasMember(date)) { //v is a value
Value d;
d=v[date];
//
//code that involves getting data from d (d is object) using HasMember
//
} else {
//generic code that has nothing to do with json
}
JSON file:
{
"main": {
"v": {
"2014-10-02" : {
//some fields
},
"2014-10-03" : {
//some fields
}
}
}
}
So the first time that i call updateData for date "2014-10-02" it runs correctly(executes the if part).
The problem is when i call updateData for another date (like "2014-10-03" that is supposed to work well) it always executes the wrong part(else part) and even when i switch back to the first date it still executes the else part. (while popping many assertions (mostly isString())).
So is HasMember really the problem here, in the sense that it is maybe altering the object?
And are there any alternative ways to search for a member, other than that?
Any tip is appreciated...
Its hard to tell without the actual code, but I think problem might be that you are treating "v" as a Value instead of an Object. "v" isn't a value, its the name of the object. So what you have is a nested object. In order to do this I think you would have to used MemberIterators and iterate through the child objects in the v object.
rapidjson has a pretty good example on how to use iterators.
there is also this question here, which has a pretty good answer on how to use nested objects
Retrieving a nested object inside a JSON string using rapidjson

How do I check if System::Collections::ArrayList exists / is empty

I'm trying to access a handle to a ::Collections::ArrayList with a two simple accessor/mutator functions:
/** --------------------------------------------
* public accessor for RX message queue
* --------------------------------------------- */
System::Collections::ArrayList^ peak_lib::rx_queue(void)
{
return this->m_Queue_Mes_RX;
}
/** --------------------------------------------
* public mutator for RX message queue
* --------------------------------------------- */
void peak_lib::rx_queue( System::Collections::ArrayList^ inList )
{
if ( inList->Count != 0 ) // <-- error line
{
this->m_Queue_Mes_RX = inList;
}
}
My compiler throws An unhandled exception of type 'System.NullReferenceException' occurred in my.exe and adds that the reference was not called on an object ( or something along these lines, I have to translate it from polish :/ ) when I try to access the ->Count property ( see error line in code ) as somebody told me to here to check if the inList variable exists.
What's the right (or at least a better :D) way to check if an ArrayList exists when I'm using C++/CLI Visual Studio 2008?
Initially, check for null before checking for count
if (inList != nullptr)
{
if(inList->count)
{}
}
Check for the null pointer before actually accessing its member.
if (inList)
{
if(inList->count)
{}
}
Also, as stated by Konrad in the comments, System::Collections::ArrayList is obsolete so try using vector instead
A property setter should either set the property to the value passed or indicate an error. There are two ways to indicate an error: Either throw an argument exception (common) or change the object to an invalid state that is exposed in future operations with the object (rare, e.g., classes intended to be used with data binding).
It could be that setting the property to null should not be an error but your question implies you want to disallow that. So, passing null where a non-null list is expected is a Boneheaded Exception. It is something that should be corrected before release and not ignored by the called code or "handled" by the calling code.
Here's the check in that case:
if (inList == nullptr) throw gcnew ArgumentNullException(L"value");
On the other hand, passing an empty list doesn't seem exceptional at all. You should consider accepting an empty list as the property value. If that doesn't make sense, perhaps you should design your class without that read-write property and instead use a method and a read-only property or the like.
Other issues:
Consider Systems::Collections::Generic::List<T> instead of System::Collections::ArrayList
Consider exposing the list as a read-only collection.

How to paste xml to C++ (Tinyxml)

I'm currently working on a project in C++ where I need to read some things from a xml file, I've figured out that tinyxml seams to be the way to go, but I still don't know exactly how to do.
Also my xml file is a little tricky, because it looks a little different for every user that needs to use this.
The xml file I need to read looks like this
<?xml version="1.0" encoding="utf-8"?>
<cloud_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx xmlns:d="http://www.kuju.com/TnT/2003/Delta" d:version="1.0">
<cCareerModel d:id="154964152">
<ScenarioCareer>
<cScenarioCareer d:id="237116344">
<IsCompleted d:type="cDeltaString">CompletedSuccessfully</IsCompleted>
<BestScore d:type="sInt32">0</BestScore>
<LastScore d:type="sInt32">0</LastScore>
<ID>
<cGUID>
<UUID>
<e d:type="sUInt64">5034713268864262327</e>
<e d:type="sUInt64">2399721711294842250</e>
</UUID>
<DevString d:type="cDeltaString">0099a0b7-e50b-45de-8a85-85a12e864d21</DevString>
</cGUID>
</ID>
</cScenarioCareer>
</ScenarioCareer>
<MD5 d:type="cDeltaString"></MD5>
</cCareerModel>
</cloud_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
Now the goal of this program is to be able to insert some string (via. a variable) and serch for the corresponding "cScenarioCarrer d:id" and read the "IsComplete" and the "BestScore".
Those strings later need to be worked with in my program, but that I can handle.
My questions here are
A. How do I go by searching for a specific "cScenarioCareer" ID
B. How do I paste the "IsComplete" and "BestScore" into some variables in my program.
Note: The xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx string is unique for every user, so keep in mind it can be anything.
If anyone out there would like to help me, I'd be very graceful, thank you.
PS. I'd like to have some kind of understanding for what I'm doing here, all though "paste this code into your program" answers are acceptable, I think it would be much better if you can tell me how and why it works.
Since you're doing this in C++ I'll make this example using the ticpp interface to
TinyXml that available at ticpp.googlecode.com.
Assumptions:
A given xml file will contain one <cloud> tag and multiple
<cCareerModel> tags.
Each <cCareerModel> contains a single <ScenarioCareer> tag which in turn contains a single <cScenarioCareer> tag
You've parsed the xml file into a TiXmlDocument called xmlDoc
You don't need to examine the data type attributes
You don't mind using exceptions
I'll also assume that you have a context variable somewhere containing a pointer to the
<cloud> tag, like so:
ticpp::Element* cloud = xmlDoc.FirstChildElement("cloud");
Here's a function that will locate the ticpp::Element for the cScenarioCareer with
the given ID.
ticpp::Element* findScenarioCareer(const std::string& careerId)
{
try
{
// Declare an iterator to access all of the cCareerModel tags and construct an
// end iterator to terminate the loop
ticpp::Iterator<ticpp::Element> careerModel;
const ticpp::Iterator<ticpp::Element> modelEnd = careerModel.end();
// Loop over the careerModel tags
for (careerModel = cloud->FirstChildElement() ; careerModel != modelEnd ;
++careerModel)
{
// Construct loop controls to access careers
ticpp::Iterator<ticpp::Element> career;
const ticpp::Iterator<ticpp::ELement> careerEnd = career.end();
// Loop over careers
for (career = careerModel->FirstChildElement("ScenarioCareer").FirstChildElement() ;
career != careerEnd ; ++career)
{
// If the the d:id attribute value matches then we're done
if (career->GetAttributeOrDefault("d:id", "") == careerId)
return career;
}
}
}
catch (const ticpp::Exception&)
{
}
return 0;
}
Then to get at the information you want you'd do something like:
std::string careerId = "237116344";
std::string completion;
std::string score;
ticpp::Element* career = findScenarioCareer(careerId);
if (career)
{
try
{
completion = career->FirstChildElement("IsCompleted")->GetText();
score = career->FirstChildElement("BestScore")->GetText();
}
catch (const ticpp::Exception&)
{
// Handle missing element condition
}
}
else
{
// Not found
}
Naturally I haven't compiled or tested any of this, but it should give you the idea.