I'm trying to get a nested IF statement to work but the ELSE statement doesn't seem to be resolving. Everything else seems to behaving correctly including the various outputs. What am I missing in my statement?
{ IF { MERGEFIELD CODE } = 0001 "Case 1"
{ IF { MERGEFIELD CODE } = 0002 "Case 2"
{ IF { MERGEFIELD CODE } = 0003 "Case 3"
{ IF { MERGEFIELD CODE } = 0004 "Case 4"
{ IF { MERGEFIELD CODE } = 0005 "Case 5"
{ IF { MERGEFIELD CODE } = 0006 "Case 6"
{ IF { MERGEFIELD CODE } = 0007 "Case 7"
}}}}}}
"{ MERGEFIELD ELSEOUTPUT }"
}
If your outputs positive numbers if valid and 0 or nothing otherwise, you could use just:
{MERGEFIELD CODE \# "'Case '0;;'{MERGEFIELD ELSEOUTPUT}'"}
Otherwise, you might use:
{IF{MERGEFIELD CODE \# 0000}= "0001" "Case 1"
{IF{MERGEFIELD CODE \# 0000}= "0002" "Case 2"
{IF{MERGEFIELD CODE \# 0000}= "0003" "Case 3"
{IF{MERGEFIELD CODE \# 0000}= "0004" "Case 4"
{IF{MERGEFIELD CODE \# 0000}= "0005" "Case 5"
{IF{MERGEFIELD CODE \# 0000}= "0006" "Case 6"
{IF{MERGEFIELD CODE \# 0000}= "0007" "Case 7" {MERGEFIELD ELSEOUTPUT}}}}}}}}
or:
{IF{MERGEFIELD CODE}= 1 "Case 1"
{IF{MERGEFIELD CODE}= 2 "Case 2"
{IF{MERGEFIELD CODE}= 3 "Case 3"
{IF{MERGEFIELD CODE}= 4 "Case 4"
{IF{MERGEFIELD CODE}= 5 "Case 5"
{IF{MERGEFIELD CODE}= 6 "Case 6"
{IF{MERGEFIELD CODE}= 7 "Case 7" {MERGEFIELD ELSEOUTPUT}}}}}}}}
The main thing wrong is the nesting - you need to relocate your
"{ MERGEFIELD ELSEOUTPUT }"
so that you have
"Case 7" "{ MERGEFIELD ELSEEOUTPUT } }
The explanation is as follows.
The syntax for an IF field is (simplified)
{ IF condition true-result false-result }
Anything after false-result will just be ignored. Try, e.g.
{ IF 1 = 1 "A" "B" "C" }
(You should see A )
and
{ IF 1 = 2 "A" "B" "C" }
(You should see B )
The "C" value will never be in the result.
Simplifying your IF statement down to a couple of cases, you have something like
{ IF X = 1 "1" { IF X = 2 "2" } { ELSEOUTPUT } }
So
the "1" is the equivalent of the "A",
the { IF X = 2 "2" } the equivalent of the "B" and
the { ELSEOUTPUT } is the equivalent of the "C"
i.e. the { ELSEOUTPUT } will never be in the result.
So you need to move your { MERGEFIELD ELSEOUTPUT } so that it is the false-result of the 0007 test, i.e.perhaps
{ IF { MERGEFIELD CODE } = 0001 "Case 1"
{ IF { MERGEFIELD CODE } = 0002 "Case 2"
{ IF { MERGEFIELD CODE } = 0003 "Case 3"
{ IF { MERGEFIELD CODE } = 0004 "Case 4"
{ IF { MERGEFIELD CODE } = 0005 "Case 5"
{ IF { MERGEFIELD CODE } = 0006 "Case 6"
{ IF { MERGEFIELD CODE } = 0007 "Case 7" "{ MERGEFIELD ELSEOUTPUT }"
}}}}}}}
or to follow your existing layout, something like
{ IF { MERGEFIELD CODE } = 0001 "Case 1"
{ IF { MERGEFIELD CODE } = 0002 "Case 2"
{ IF { MERGEFIELD CODE } = 0003 "Case 3"
{ IF { MERGEFIELD CODE } = 0004 "Case 4"
{ IF { MERGEFIELD CODE } = 0005 "Case 5"
{ IF { MERGEFIELD CODE } = 0006 "Case 6"
{ IF { MERGEFIELD CODE } = 0007 "Case 7"
"{ MERGEFIELD ELSEOUTPUT }"
}}}}}}}
Beyond that, whether the IF will work exactly as you intend depends on what values the CODE field can have and how you want each value to be treated.
As long as your codes are all 4-digit numeric codes, or they are numeric codes and you want "1", "01" etc. to be treated the same way as "0001" then the IF field should work as it is. Or you could simplify it in various ways.
In the rather less common situation where you need "1" to be treated differently from "0001", you would need to quote the { MERGEFIELD CODE } field, e.g. assuming "1" needs to result in ELSEOUTPUT you need
{ IF "{ MERGEFIELD CODE }" = 0001 "Case 1"
{ IF "{ MERGEFIELD CODE }" = 0002 "Case 2"
{ IF "{ MERGEFIELD CODE }" = 0003 "Case 3"
{ IF "{ MERGEFIELD CODE }" = 0004 "Case 4"
{ IF "{ MERGEFIELD CODE }" = 0005 "Case 5"
{ IF "{ MERGEFIELD CODE }" = 0006 "Case 6"
{ IF "{ MERGEFIELD CODE }" = 0007 "Case 7"
"{ MERGEFIELD ELSEOUTPUT }"
}}}}}}}
Although to make everything as clear as possible I would favour
{ IF "{ MERGEFIELD CODE }" = "0001" "Case 1"
{ IF "{ MERGEFIELD CODE }" = "0002" "Case 2"
{ IF "{ MERGEFIELD CODE }" = "0003" "Case 3"
{ IF "{ MERGEFIELD CODE }" = "0004" "Case 4"
{ IF "{ MERGEFIELD CODE }" = "0005" "Case 5"
{ IF "{ MERGEFIELD CODE }" = "0006" "Case 6"
{ IF "{ MERGEFIELD CODE }" = "0007" "Case 7"
"{ MERGEFIELD ELSEOUTPUT }"
}}}}}}}
Quoting the "0001" and not quoting the "{ MERGEFIELD CODE }" means that the comparison is still numeric and won't work as intended.
That will also deal with two other "edge" cases, i.e.
If your codes can be alphanumeric, if you do not quote the { MERGEFIELD CODE } field, codes like "3ABC", "03AB" and even "01+2" will
also match with 0003.
If { MERGEFIELD CODE } resolves to the name of a bookmark in your
mail merge main document and you do not quote the field or the other
comparand, the IF field will actually compare the value of that
bookmark. Yes, it's a really weird quirk of the IF field, but it's
why I favour quoting anything that you want to be treated as
alphanumeric.
So e.g. if { MERGEFIELD CODE } has value "ABCD" and somewhere before your { IF } field you have { SET ABCD 4 } if you have
{ IF { MERGEFIELD CODE } = 0004 "Case 4" "{ MERGEFIELD ELSEOUTPUT }" }
then the result will be Case 4, not the ELSEOUTPUT value.
Related
I am fairly new to boost::property_tree and I am having a little trouble with what should be a simple task.
I have a default xml file of which is to be copied and made unique with parameters passed into via the ptree & modelModifier(...) function below. All I want to do is parse the xml into a ptree, and then modify the name field (amongst others, but lets just start with name) from "default" to whatever name is passed in from the object_name variable, then write that back into the original ptree.
The function pTreeIterator just iterates through each child and displays its contents.
xml
<?xml version='1.0'?>
<sdf version='1.7'>
<model name='default'>
<link name='link'>
<inertial>
<mass>3.14999</mass>
<inertia>
<ixx>2.86712</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>2.86712</iyy>
<iyz>0</iyz>
<izz>0.524998</izz>
</inertia>
<pose>0 0 0 0 -0 0</pose>
</inertial>
</link>
</model>
</sdf>
Code
void ptreeIterator(ptree & pt)
{
using boost::property_tree::ptree;
for (auto&it : pt)
{
std::cout << __FUNCTION__ << std::endl;
std::cout << "------------------------------------------------------" << std::endl;
std::cout << "Iteration output: " << std::endl;
std::cout << "1: pt1: " << it.first << std::endl;
if(pt.get_child_optional(it.first))
{
cout << "pt.get_child_optional(it.first) ---> " << it.first << endl;
ptree pt2 = pt.get_child(it.first);
for (auto&it2 : pt2)
{
std::cout << "\t2: " << "pt2: " << it2.first << " :: " << (std::string)it2.second.data() << std::endl;
if(pt2.get_child_optional(it2.first))
{
ptree pt3 = pt2.get_child(it2.first);
for (auto&it3 : pt3)
{
std::cout << "\t\t3: " << "pt3: " << it3.first << " :: " << (std::string)it3.second.data() << std::endl;
}
}
}
}
}
}
ptree & modelModifier(ptree &model, double px, double py, std::string object_name, uint16_t height)
{
for(auto &it:model){
cout << "it.first = " << it.first << endl;
if(it.first=="model"){
cout << "MODEL TAG" << endl;
model.put_value(object_name);
}
ptreeIterator(model);
}
}
int main(){
ptree ptModel;
const string filenameToInsert = "model.sdf";
std::ifstream ifsModel(filenameToInsert,std::ifstream::binary);
read_xml(ifsModel, ptModel, boost::property_tree::xml_parser::trim_whitespace);
modelModifier(ptModel, 0, 0, "TEST1234567", 30);
return 0;
}
Output
it.first = model
it.second.data()
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: model
pt.get_child_optional(it.first) ---> model
2: pt2: <xmlattr> ::
3: pt3: name :: default
Expected Output
it.first = model
it.second.data()
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: model
pt.get_child_optional(it.first) ---> model
2: pt2: <xmlattr> ::
3: pt3: name :: TEST1234567
Firstly, your code has UB, since modelModifier doesn't return a value.
The C-style cast in (std::string)it2.second.data() is extremely dangerous as it risks reinterpret_cast-ing unrelated types. There is no reason whatsoever for this kind of blunt casting. Just remove the cast!
Also, ptreeIterator should probably take a ptree const&, not ptree&.
With these fixed, the sample does NOT show the output you claim, instead it prints (Live On Coliru)
it.first = sdf
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: sdf
pt.get_child_optional(it.first) ---> sdf
2: pt2: <xmlattr> ::
3: pt3: version :: 1.7
2: pt2: model ::
3: pt3: <xmlattr> ::
3: pt3: link ::
Now even in your question output, you clearly see the difference between the model node and its name attribute, which apparently you want to modify. Just write the code to access that:
it.second.get_child("<xmlattr>.name").put_value(object_name);
This would be correct, assuming that the attribute always exists and instead of ptModel you pass ptModel.get_child("sdf") to modifyModel).
Other Notes: SIMPLIFY!
That said, please simplify the whole thing!
ptree pt2 = pt.get_child(it.first);
Should have been something like
ptree const& pt2 = it.second;
And
the use of get_child_optional only to repeat with get_child is even more wasteful
Good practice is to separate output/query and mutation. So don't call ptreeIterator from inside modelModifier
Also, give functions a good descriptive name (so that you don't have sheepishly explain "The function pTreeIterator just iterates through each child and displays its contents" - just call it displayModel?)
Instead of painstakingly (and flawed) iterating the particular model and printing it in pretty confusing bespoke manner, just use write_xml/write_info/write_json to dump it in a reliable manner.
Listing
Live On Coliru
namespace Model {
void display(ptree const& pt)
{
write_json(std::cout << __FUNCTION__ << "\n---------------\n", pt);
}
void modify(ptree& model, double, double, std::string object_name, uint16_t)
{
for (auto& it : model) {
std::cout << "root = " << it.first << std::endl;
it.second.get_child("model.<xmlattr>.name").put_value(object_name);
}
}
}
Prints
root = sdf
display
---------------
{
"sdf": {
"<xmlattr>": {
"version": "1.7"
},
"model": {
"<xmlattr>": {
"name": "TEST1234567"
},
"link": {
"<xmlattr>": {
"name": "link"
},
"inertial": {
"mass": "3.14999",
"inertia": {
"ixx": "2.86712",
"ixy": "0",
"ixz": "0",
"iyy": "2.86712",
"iyz": "0",
"izz": "0.524998"
},
"pose": "0 0 0 0 -0 0"
}
}
}
}
}
Bonus
In the case that the name attribute might not already exist, the following code would create it on the fly:
void modify(ptree& model, double, double, std::string object_name, uint16_t)
{
ptree value;
value.put_value(object_name);
for (auto& it : model) {
std::cout << "root = " << it.first << std::endl;
it.second.put_child("model.<xmlattr>.name", value);
}
}
I have a complex JSON to load into a data structure in C++11 and I got high recommendations about RapidJSON. I need to iterate over a complex JSON and looked around for answers on how to do it. The best answer I found was in this thread.
However, there's a small glitch in matching this solution to mine, I have members in the JSON that have different names but identical content:
"responsibilities": {
"starters_reciepe": {
"name": "bok choi salad",
"type": "veggie",
"ingredients": {
"leafyIng": "bok choi",
"proteinIng": "tofu",
"seasoning": [
{
"2 tsp": "salt",
"1 tsp": "turmric"
}
]
}
},
"mainCourse_reciepe": {
"name": "pad tai",
"type": "yum yum",
"ingredients": {
"leafyIng": "chard",
"proteinIng": "soylent green"
"seasoning": [
{
"2 tsp": "black pepper",
"1 tsp": "tears of the angels"
}
]
}
}
}
Basically, I need to go over the content of the ingredients, but I can't get over the fact that starters_reciepe is not like mainCourse_reciepe.
EDITED:
Here's my code:
Document d;
ifstream in("TestingJSON.json", ios::binary);
if (!in)
throw runtime_error("Failed to open file");
istreambuf_iterator<char> head(in);
istreambuf_iterator<char> tail;
string data(head, tail);
d.Parse(data.c_str());
const Value& prog = d["responsibilities"];
for (Value::ConstValueIterator p = prog.Begin(); p != prog.End(); ++p) {
cout << (*p)["iUniqueID"].GetString()<<endl;
const Value& inFiles = (*p)["inFiles"];
for (Value::ConstValueIterator inFile = inFiles.Begin(); inFile != prog.End(); ++inFile) {
cout << (*inFile)["sFileType"].GetString() << endl;
cout << (*inFile)["pos"]["x1"].GetInt() << endl;
}
}
Can I use wildcards and write *_reciepe?
I could find anything on RapidJSON and wildcards. Is this even a possibility?
Always validate your raw JSON with linters (e.g. https://jsonlint.com/). This JSON in your question is not valid. You need to fix that.
The "responsibilites" object in your JSON contains recipes only. I'm not sure why you need to compare it with *_recipe. But, given the example below, you may easily implement that comparison if needed. This thread might be helpful in this regard.
You can use C++11's range-based for loop for these iterations. Just take care of the correct type you want to use/manipulate according to your use-case. If in doubt, consult rapidjson's tutorial and documentation.
Here's an example with raw literal string as JSON input:
#include <iostream>
#include <rapidjson/document.h>
int main()
{
constexpr auto data = R"json(
{
"responsibilities": {
"starters_recipe": {
"name": "bok choi salad",
"type": "veggie",
"ingredients": {
"leafyIng": "bok choi",
"proteinIng": "tofu",
"seasoning": [{
"2 tsp": "salt",
"1 tsp": "turmric"
}]
}
},
"mainCourse_recipe": {
"name": "pad tai",
"type": "yum yum",
"ingredients": {
"leafyIng": "chard",
"proteinIng": "soylent green",
"seasoning": [{
"2 tsp": "black pepper",
"1 tsp": "tears of the angels"
}]
}
}
}
}
)json";
rapidjson::Document doc;
doc.Parse( data );
const auto& courses = doc["responsibilities"].GetObject();
for ( const auto& course : courses )
{
const auto& course_name = course.name.GetString();
const auto& recipe = courses[course_name].GetObject();
const auto& recipe_name = recipe["name"].GetString();
const auto& ingredients = recipe["ingredients"].GetObject();
const auto& leafyIng = ingredients["leafyIng"].GetString();
const auto& proteinIng = ingredients["proteinIng"].GetString();
const auto& seasoning = ingredients["seasoning"].GetArray()[0].GetObject();
std::cout << "Course: " << course_name << '\n'
<< "Recipe: " << recipe_name << '\n'
<< "Ingredients:\n"
<< "- Leaf : " << leafyIng << '\n'
<< "- Protein : " << proteinIng << '\n'
<< "- Seasoning:\n";
for ( const auto& s : seasoning )
{
const auto& k = s.name.GetString();
const auto& v = s.value.GetString();
std::cout << " - " << k << ", " << v << '\n';
}
std::cout << '\n';
}
return 0;
}
Output:
Course: starters_recipe
Recipe: bok choi salad
Ingredients:
- Leaf : bok choi
- Protein : tofu
- Seasoning:
- 2 tsp, salt
- 1 tsp, turmric
Course: mainCourse_recipe
Recipe: pad tai
Ingredients:
- Leaf : chard
- Protein : soylent green
- Seasoning:
- 2 tsp, black pepper
- 1 tsp, tears of the angels
The "seasoning" array contains only one object that's why this line refers to the 0th index:
const auto& seasoning = ingredients["seasoning"].GetArray()[0].GetObject();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
I guess that you intended to have an array of objects, not an array with a single object.
This:
"seasoning": [
{ "2 tsp": "black pepper" },
{ "1 tsp": "tears of the angels" }
]
and, not this:
"seasoning": [{
"2 tsp": "black pepper",
"1 tsp": "tears of the angels"
}]
You have to manipulate this accordingly in code also.
If you need to check only if a string ends with a known value this is quite simple to do directly comparing it without wildcards libraries:
auto& obj = doc["responsibilities"];
std::string suffix = "_reciepe";
for (auto p = obj.MemberBegin(); p != obj.MemberEnd(); ++p) {
auto& member_name = p->name;
if (member_name.GetStringLength() >= suffix.length()) {
if (memcmp(member_name.GetString() + member_name.GetStringLength() - suffix.length(), suffix.c_str(), suffix.length()) == 0) {
// Process matching node
std::cout << p->value["name"].GetString() << std::endl;
}
}
}
If you need to match against more complex patterns then you can use std::regex
im trying to use a regular expression to ensure the heading entered for an aircraft is between o and 360, but i cant get it to work.
std::regex headingCheck{"^([0-9]|1[0-9]2[0])$"};
bool match = false;
while (!match)
{
if (std::regex_match(heading, headingCheck))
{
heading_ = heading;
}
else
{
std::cout << "Invalid heading, can only be between 0 and 360 degrees" << std::endl;
}
}
#
//Heading can only be between 0-360?
if (heading >= 0 && heading <= 360)
{
heading_ = heading;
}
else
{
std::cout << "Incorrect heading, heading can only be between 0 and 360" << std::endl;
}
Should i do this instead? Is it as accurate/reliable?
If you still want regex (though this is not optimal) you can use the following expression:
std::regex re{ "^[0-9]$|^[1-9][0-9]$|^[1-2][0-9][0-9]$|^3[0-5][0-9]$|^360$" };
std::string headings[5] = { "0","15","390","360","23883" };
for (int i = 0; i < 5; ++i)
std::cout << "Heading " << headings[i] << " is " << (std::regex_match(headings[i], re) ? "valid" : "invalid") << std::endl;
First part matches 0-9, second part matches 10-99, third part matches 100-299, fourth part matches 300-359, last matches 360. Prints:
Heading 0 is valid
Heading 15 is valid
Heading 390 is invalid
Heading 360 is valid
Heading 23883 is invalid
I am doing a project with a Pascal subset. My code looks like:
NLINE [\n]
BRACKET ['('|')']
%%
{BRACKET} {
std::cout << "Found BRACKET symbol " << yytext[0] << std::endl;
return yytext[0];
}
{NLINE} {
std::cout << "Found NEWLINE symbol " << yytext[0] << std::endl;
yylineno++;
}
...
. { // anything is exactly before EOF
std::cout << "Found ANYTHING " << yytext[0] << std::endl;
yylval = NONE;
return yytext[0];
}
I tried many ways to deal with that, also just \n instead of [\n] or [ \n] but without the expected results. Below is the output:
...
Found BRACKET symbol )
Found ANYTHING ;
Found ANYTHING << where in code should be \n
I know that this is \n issue, because when I push the code without that it works like a charm!
Will appreciate every constructive answer.
The problem seemed to be the carriage return symbol. In case you will have similar issue, if this won't help, you should check other nonprintable characters.
In my case helped:
DELIM [ \t\r]
DELIM is "eated" in my solution so no rule is applied.
In the following code, "situation 1" works as expected on all
compilers tested, however "situation 2" it seems to behave differently
based on the compiler used.
As an example MSVC has sit1 and sit2 produce the same results, however
when using gcc/clang and libstdc++, the modification occurs to the
original string and it's copy (sort of like a COW string) even though
I'm building using the C++11 switch.
#include <iostream>
#include <string>
int main() {
// situation 1
{
std::string x0 = "12345678";
std::string x1 = x0;
char* ptr = &x0[0] + 3;
(*ptr) = ' ';
std::cout << "1. x0: " << x0 << "\n";
std::cout << "1. x1: " << x1 << "\n";
if ((&x0[0]) == x0.data()) std::cout << "1. ptrs are equal\n";
}
// situation 2
{
std::string x0 = "12345678";
std::string x1 = x0;
char* ptr = const_cast<char*>(x0.data() + 3);
(*ptr) = ' ';
std::cout << "2. x0: " << x0 << "\n";
std::cout << "2. x1: " << x1 << "\n";
if ((&x0[0]) == x0.data()) std::cout << "2. ptrs are equal\n";
}
return 0;
}
GCC (6.1)
1. x0: 123 5678
1. x1: 12345678
1. ptrs are equal
2. x0: 123 5678
2. x1: 123 5678
2. ptrs are equal
MSVC (2015)
1. x0: 123 5678
1. x1: 12345678
1. ptrs are equal
2. x0: 123 5678
2. x1: 12345678
2. ptrs are equal
Is there any reason for the discrepancies in behavior between the various compilers - given that &x0[0] and .data() return the same address?
Situation 2 causes undefined behaviour:
char* ptr = const_cast<char*>(x0.data() + 3);
(*ptr) = 'Z';
According to the specification of std::basic_string::data (C++14 [string.accessors]/3):
Requires: The program shall not alter any of the values stored in the character array.
In other words, you are not allowed to cast away the const and modify the string via the pointers returned by data() or c_str() .