How to find a string is Json or not using Jansson? - c++

I am using Jansson.
bool ConvertJsontoString(string inputText, string& OutText)
{
/* Before doing anything I want to check
if the inputText is a valid json string or not */
}

Why don't you read the documentation where it clearly states:
json_t *json_loads(const char *input, size_t flags, json_error_t *error)
Return value: New reference.
Decodes the JSON string input and returns the array or object it contains,
or NULL on error, in which case error is filled with information about the error.
flags is described above.
Also they even provide an example on how to use this:
root = json_loads(text, 0, &error);
free(text);
if(!root)
{
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
return 1;
}

Related

Remove carriage return from json text

I get json text returned from an api call and I run it trough a script JSON Serialization and Deserialization from here https://www.mql5.com/en/code/13663.
My issue is that it only process the first few parts of the json because of what I believe to a linebreak/carriage inside the json structure. I dont get any error message, just an array containing everything before that line break.
I dont want to remove the line breaks inside the text fields in the json, only the returns inside the json structure. It is in the place each time just after {"ok":true,"result":[{"update_id":568022212,
Here is a full section
{"ok":true,"result":[{"update_id":568022212,
"channel_post":{"message_id":436,"chat":{"id":-1001436032340,"title":"FORTUNA","type":"channel"},"date":1588899840,"reply_to_message":{"message_id":372,"chat":{"id":-1001436032340,"title":"FORTUNA","type":"channel"},"date":1588838583,"text":"A\nGbpusd buy now 1.2360\nSl 1.2280 \nTp open\n","entities":[{"offset":52,"length":11,"type":"mention"}]},"text":"A\n42 pips bookd close\ud83d\udfe2\ud83d\udfe2"}},{"update_id":568022213,
"channel_post":{"message_id":437,"chat":{"id":-1001436032340,"title":"FORTUNA","type":"channel"},"date":1588900321,"reply_to_message":{"message_id":435,"chat":{"id":-1001436032340,"title":"FORTUNA","type":"channel"},"date":1588893671,"text":"A\nGold buy 1713.3\nSl 1702 \nTp open\nSwing trade"},"text":"Amazon\n60 pips bookd close\ud83d\udfe2\ud83d\udfe2"}},{"update_id":568022214,
"channel_post":{"message_id":438,"chat":{"id":-1001436032340,"title":"FORTUNA","type":"channel"},
MQL4 code:
int GetUpdates()
{
if(m_token==NULL)
return(ERR_TOKEN_ISEMPTY);
string out;
string url=StringFormat("%s/bot%s/getUpdates",TELEGRAM_BASE_URL,m_token);
string params=StringFormat("offset=%d",m_update_id);
//---
int res=PostRequest(out,url,params,WEB_TIMEOUT);
if(res==0)
{
Print(StringDecode(out));
//--- parse result
CJAVal js(NULL,jtUNDEF);
bool done=js.Deserialize(out);
if(!done)
return(ERR_JSON_PARSING);
bool ok=js["ok"].ToBool();
if(!ok)
return(ERR_JSON_NOT_OK);
CCustomMessage msg;
int total=ArraySize(js["result"].m_e);
for(int i=0; i<total; i++)
{
CJAVal item=js["result"].m_e[i];
//---
msg.update_id=item["update_id"].ToInt();
//---
msg.message_id=item["message"]["message_id"].ToInt();
msg.message_date=(datetime)item["message"]["date"].ToInt();
//---
msg.message_text=item["message"]["text"].ToStr();
printf((datetime)item["message"]["date"].ToInt());
msg.message_text=StringDecode(msg.message_text);
//---
msg.from_id=item["message"]["from"]["id"].ToInt();
msg.from_first_name=item["message"]["from"]["first_name"].ToStr();
msg.from_first_name=StringDecode(msg.from_first_name);
msg.from_last_name=item["message"]["from"]["last_name"].ToStr();
msg.from_last_name=StringDecode(msg.from_last_name);
msg.from_username=item["message"]["from"]["username"].ToStr();
msg.from_username=StringDecode(msg.from_username);
//---
msg.chat_id=item["message"]["chat"]["id"].ToInt();
msg.chat_first_name=item["message"]["chat"]["first_name"].ToStr();
msg.chat_first_name=StringDecode(msg.chat_first_name);
msg.chat_last_name=item["message"]["chat"]["last_name"].ToStr();
msg.chat_last_name=StringDecode(msg.chat_last_name);
msg.chat_username=item["message"]["chat"]["username"].ToStr();
msg.chat_username=StringDecode(msg.chat_username);
I tried to use the same library, it seems to have a bug with parsing arrays. That is why I used https://www.mql5.com/en/code/11134. It has a disadvantage: you need to delete all the objects, unfortunately; as a result there's plenty of code. But at least it works.
Seems your json has incorrect format, I used to cut it a little.
#include <json1.mqh> //modified version, https://www.mql5.com/en/forum/28928/page5#comment_15766620
//const string post_result=
//{"ok":true,"result":[
//{"update_id":568022212,"channel_post":{"message_id":436,"chat":{"id":-1001436032340,"title":"FORTUNA","type":"channel"},
//"date":1588899840,"reply_to_message":{"message_id":372,"chat":{"id":-1001436032340,"title":"FORTUNA","type":"channel"},
//"date":1588838583,"text":"A\nGbpusd buy now 1.2360\nSl 1.2280 \nTp open\n","entities":[{"offset":52,"length":11,"type":"mention"}]},"text":"A\n42 pips bookd close\ud83d\udfe2\ud83d\udfe2"}},
//{"update_id":568022213,"channel_post":{"message_id":437,"chat":{"id":-1001436032340,"title":"FORTUNA","type":"channel"},
//"date":1588900321,"reply_to_message":{"message_id":435,"chat":{"id":-1001436032340,"title":"FORTUNA","type":"channel"},
//"date":1588893671,"text":"A\nGold buy 1713.3\nSl 1702 \nTp open\nSwing trade"},"text":"Amazon\n60 pips bookd close\ud83d\udfe2\ud83d\udfe2" }}]}
//;
void function()
{
//---
const string post_result=getContent();//ok, you managed to get some string here
JSONParser *parser=new JSONParser();
JSONValue *value=parser.parse(post_result);
delete(parser);
if(CheckPointer(value)==POINTER_INVALID)
{
printf("%i %s: error!",__LINE__,__FILE__);
delete(value);
return;
}
JSONObject *obj=(JSONObject*)value;
const bool isSuccess=obj.getBool("ok");
printf("%i %s: isSuccess=%d",__LINE__,__FILE__,isSuccess);
if(!isSuccess)return;
JSONValue *resultValue=obj.getValue("result");
if(CheckPointer(resultValue)!=POINTER_INVALID)
{
JSONArray *resultValueArray=resultValue;
for(int i=0;i<resultValueArray.size();i++)
{
printf("%i %s: #%d=%s",__LINE__,__FILE__,i,resultValueArray.getObject(i).toString());
//you can work with JSONObject or with string, whatever is more convenient
parseResultLine(resultValueArray.getObject(i));
}
delete(resultValueArray);
}
delete(resultValue);
delete(obj);
}
bool parseResultLine(JSONObject *object)
{
const long update_id=object.getLong("update_id");
JSONObject *channel_post=object.getObject("channel_post");
const long message_id=channel_post.getLong("message_id");
const datetime date=(datetime)channel_post.getInt("date");
const string text=channel_post.getString("text");
printf("%i %s: id=%s, dt=%d/%s, text=%s",__LINE__,__FILE__,IntegerToString(message_id),(int)date,TimeToString(date),text);
JSONObject *chat=channel_post.getObject("chat");
const long chat_id=chat.getLong("id");
const string chat_title=chat.getString("title");
printf("%i %s: chat-> id=%I64d title=%s type=%s",__LINE__,__FILE__,chat_id,chat_title,chat.getString("type"));
JSONObject *reply=channel_post.getObject("reply_to_message");
printf("%i %s: replied: id=%s, date=%s",__LINE__,__FILE__,string(reply.getLong("message_id")),TimeToString(reply.getInt("date")));
return true;
}

Embarcadero: How to use TBase64Encoding's EncodeBytesToString method

I am attempting to convert an array of bytes to a base64 encoded String using the EncodeBytesToString method of the TBase64Encoding class. The documentation for EncodeBytesToString states:
"Returns a string with the input array of bytes encoded up to the specified number of bytes."
Therefore, I attempted to encode my byte array like so:
TFile * File = new TFile();
TBytes Bytes = File->ReadAllBytes("D:\\Sample.pdf");
TBase64Encoding * Encoder = new TBase64Encoding();
String EncodedBytes = Encoder->EncodeBytesToString(Bytes, Bytes.Length);
However, I get the following error:
E2285 Could not find a match for 'TNetEncoding::EncodeBytesToString(TByteDynArray,int)'
I am confused, as the documentation seems to say that I should pass a TBytes object and an int into this function. What am I missing here?
Try this:
//------------------------------------------------------------------------------
String __fastcall BytesToBase64( TByteDynArray _ArrayIn )
{
TBase64Encoding * Encoding = new TBase64Encoding( 64, '\n' );
String Result = Encoding->EncodeBytesToString( &_ArrayIn[0], _ArrayIn.High );
delete Encoding;
return Result;
}
//------------------------------------------------------------------------------
TByteDynArray __fastcall Base64ToBytes( String _64String )
{
TByteDynArray My64Bytes = _64String.BytesOf();
return TNetEncoding::Base64->Decode(&My64Bytes[0], My64Bytes.High);
}
//------------------------------------------------------------------------------
System.NetEncoding.TNetEncoding provides the static property Base64 to retrieve an instance of TNetEncoding for base64 encoding.
So this will also work:
String __fastcall BytesToBase64(TByteDynArray _ArrayIn)
{
return TNetEncoding::Base64->EncodeBytesToString(&_ArrayIn[0], _ArrayIn.High);
}

UTF-8 to UCS-2 conversion with icu library

I'm currently working on and hitting an issue with converting a UTF-8 string to a UCS-2 string with the icu library. There are several number of ways to do this in the library, but so far none of them seem to be working, but considering the popularity of this library I'm under the assumption that I'm doing something wrong.
First off is the common code. In all cases I'm creating and passing a string on an object, but until it reaches the conversion steps there is no manipulation.
The currently utf-8 string being used is simply "ĩ".
For the sake of simplicity I'll represent the string being used as uniString in this code
UErrorCode resultCode = U_ZERO_ERROR;
UConverter* m_pConv = ucnv_open("ISO-8859-1", &resultCode);
// Change the callback to error out instead of the default
const void* oldContext;
UConverterFromUCallback oldFromAction;
UConverterToUCallback oldToAction;
ucnv_setFromUCallBack(m_pConv, UCNV_FROM_U_CALLBACK_STOP, NULL, &oldFromAction, &oldContext, &resultCode);
ucnv_setToUCallBack(m_pConv, UCNV_TO_U_CALLBACK_STOP, NULL, &oldToAction, &oldContext, &resultCode);
int32_t outputLength = 0;
int bodySize = uniString.length();
int targetSize = bodySize * 4;
char* target = new char[targetSize];
printf("Body: %s\n", uniString.c_str());
if (U_SUCCESS(resultCode))
{
// outputLength = ucnv_convert("ISO-8859-1", "UTF-8", target, targetSize, uniString.c_str(), bodySize, &resultCode);
outputLength = ucnv_fromAlgorithmic(m_pConv, UCNV_UTF8, target, targetSize, uniString.c_str(),
uniString.length(), &resultCode);
ucnv_close(m_pConv);
}
printf("ISO-8859-1 DGF just tried to convert '%s' to '%s' with error '%i' and length '%i'", uniString.c_str(),
outputLength ? target : "invalid_char", resultCode, outputLength);
if (resultCode == U_INVALID_CHAR_FOUND || resultCode == U_ILLEGAL_CHAR_FOUND || resultCode == U_TRUNCATED_CHAR_FOUND)
{
if (resultCode == U_INVALID_CHAR_FOUND)
{
printf("Unmapped input character, cannot be converted to Latin1");
m_pConv = ucnv_open("UCS-2", &resultCode);
if (U_SUCCESS(resultCode))
{
// outputLength = ucnv_convert("UCS-2", "UTF-8", target, targetSize, uniString.c_str(), bodySize, &resultCode);
outputLength = ucnv_fromAlgorithmic(m_pConv, UCNV_UTF8, target, targetSize, uniString.c_str(),
uniString.length(), &resultCode);
ucnv_close(m_pConv);
}
printf("UCS-2 DGF just tried to convert '%s' to '%s' with error '%i' and length '%i'", uniString.c_str(),
outputLength ? target : "invalid_char", resultCode, outputLength);
if (U_SUCCESS(resultCode))
{
pdus = SegmentText(target, pText, SEGMENT_SIZE_UNICODE_MAX, true);
}
}
else
{
printf("DecodeText(): Text contents does not appear to be valid UTF-8");
}
}
else
{
printf("DecodeText(): Text successfully converted to Latin1");
std::string newBody(target, outputLength);
pdus = SegmentText(newBody, pPdu, SEGMENT_SIZE_MAX);
}
The problem is the ucnv_fromAlgorithmic function is throwing an error U_INVALID_CHAR_FOUND for the ucs-2 conversion. This makes sense for the ISO-8859-1 attempt, but not the ucs-2.
The other attempt was to use ucnv_convert which you can see is commented out. This function attempted conversion, but didn't fail on the ISO-8859-1 attempt as it should.
So the question is, does anyone have experience with these function and see something incorrect or is there something incorrect about the assumption of conversion for this character?
You need to reset resultCode to U_ZERO_ERROR before calling ucnv_open. Quote from manual:
"ICU functions that take a reference (C++) or a pointer (C) to a UErrorCode first test if(U_FAILURE(errorCode)) { return immediately; } so that in a chain of such functions the first one that sets an error code causes the following ones to not perform any operation"

another C++ equivalent for the followingtwo lines of code

Sorry for such a vague title.
Basically, I am trying to hack a function to suit my needs. But lately I have been working a lot on python and my c++ is bit rusty.
So earlier my function took a
int func(FILE *f)
{ .....
if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL)
return MM_PREMATURE_EOF;
if (sscanf(line, "%s %s %s %s %s", banner, mtx, crd, data_type,
storage_scheme) != 5)
return MM_PREMATURE_EOF;
}
now instead of this I am directly inputing the string data
int func(std::string *data)
{ .....
// how should I modify this if statment..I want to parse the same file
// but instead it is in form of one giant string
}
Thanks
You can use the same code, just convert the data in the std::string into a C string.
sscanf(data->c_str(), "%s %s %s %s %s", //...);
However, you should consider passing in a const reference, since you are probably not planning on modifying your input data:
int func(const std::string &data) {
//...
if (sscanf(data.c_str(), //...)) {
//...
}
}

RapidXML, reading and saving values

I've worked myself through the rapidXML sources and managed to read some values. Now I want to change them and save them to my XML file:
Parsing file and set a pointer
void SettingsHandler::getConfigFile() {
pcSourceConfig = parsing->readFileInChar(CONF);
cfg.parse<0>(pcSourceConfig);
}
Reading values from XML
void SettingsHandler::getDefinitions() {
SettingsHandler::getConfigFile();
stGeneral = cfg.first_node("settings")->value();
/* stGeneral = 60 */
}
Changing values and saving to file
void SettingsHandler::setDefinitions() {
SettingsHandler::getConfigFile();
stGeneral = "10";
cfg.first_node("settings")->value(stGeneral.c_str());
std::stringstream sStream;
sStream << *cfg.first_node();
std::ofstream ofFileToWrite;
ofFileToWrite.open(CONF, std::ios::trunc);
ofFileToWrite << "<?xml version=\"1.0\"?>\n" << sStream.str() << '\0';
ofFileToWrite.close();
}
Reading file into buffer
char* Parser::readFileInChar(const char* p_pccFile) {
char* cpBuffer;
size_t sSize;
std::ifstream ifFileToRead;
ifFileToRead.open(p_pccFile, std::ios::binary);
sSize = Parser::getFileLength(&ifFileToRead);
cpBuffer = new char[sSize];
ifFileToRead.read( cpBuffer, sSize);
ifFileToRead.close();
return cpBuffer;
}
However, it's not possible to save the new value. My code is just saving the original file with a value of "60" where it should be "10".
Rgds
Layne
I think this is a RapidXML Gotcha
Try adding the parse_no_data_nodes flag to cfg.parse<0>(pcSourceConfig)
You should definitely be testing that the output file opened correctly and that your write succeeded. At the simplest, you need something like:
if ( ! ofFileToWrite << "<?xml version=\"1.0\"?>\n"
<< sStream.str() << '\0' ) {
throw "write failed";
}
Note that you don't need the '\0' terminator, but it shouldn't do any harm.
Use the following method to add an attribute to a node. The method uses the allocation of memory for strings from rapidxml. So rapidxml takes care of the strings as long as the document is alive. See http://rapidxml.sourceforge.net/manual.html#namespacerapidxml_1modifying_dom_tree for further information.
void setStringAttribute(
xml_document<>& doc, xml_node<>* node,
const string& attributeName, const string& attributeValue)
{
// allocate memory assigned to document for attribute value
char* rapidAttributeValue = doc.allocate_string(attributeValue.c_str());
// search for the attribute at the given node
xml_attribute<>* attr = node->first_attribute(attributeName.c_str());
if (attr != 0) { // attribute already exists
// only change value of existing attribute
attr->value(rapidAttributeValue);
} else { // attribute does not exist
// allocate memory assigned to document for attribute name
char* rapidAttributeName = doc.allocate_string(attributeName.c_str());
// create new a new attribute with the given name and value
attr = doc.allocate_attribute(rapidAttributeName, rapidAttributeValue);
// append attribute to node
node->append_attribute(attr);
}
}