Skip to content

Commit

Permalink
Allow escaped json to survive rendering (#5)
Browse files Browse the repository at this point in the history
* add option to .dump() strings instead of .get<std::string>()

* strip leading and trailing " to preserve expected behavior

* add comments

* update single include

* don't run push CI

* remove R formatting attempting to appease windows compiler

* put push back to match upstream and reduce complexity
  • Loading branch information
jbohanon authored Jul 21, 2023
1 parent e35f106 commit 2c441a3
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 2 deletions.
1 change: 1 addition & 0 deletions include/inja/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ struct ParserConfig {
*/
struct RenderConfig {
bool throw_at_missing_includes {true};
bool escape_strings {};
};

} // namespace inja
Expand Down
5 changes: 5 additions & 0 deletions include/inja/environment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ class Environment {
lexer_config.notation = notation;
}

/// Sets the config for rendering strings raw or escaped
void set_escape_strings(bool escape_strings) {
render_config.escape_strings = escape_strings;
}

/// Sets the element notation syntax
void set_search_included_templates_in_files(bool search_in_files) {
parser_config.search_included_templates_in_files = search_in_files;
Expand Down
15 changes: 14 additions & 1 deletion include/inja/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,20 @@ class Renderer : public NodeVisitor {

void print_data(const std::shared_ptr<json> value) {
if (value->is_string()) {
*output_stream << value->get_ref<const json::string_t&>();
std::string val;
if (config.escape_strings) {
// get the value as a dump() to properly escape values
val = value->dump();

// strip the leading and trailing " characters that are added by dump()
// if C++20 is adopted, val.starts_with and val.ends_with would clean this up a bit
val = val.substr(0,1) == "\"" && val.substr(val.length()-1,1) == "\""
? val.substr(1, val.length()-2)
: val;
} else {
val = value->get_ref<const json::string_t&>();
}
*output_stream << val;
} else if (value->is_number_integer()) {
*output_stream << value->get<const json::number_integer_t>();
} else if (value->is_null()) {
Expand Down
21 changes: 20 additions & 1 deletion single_include/inja/inja.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,7 @@ struct ParserConfig {
*/
struct RenderConfig {
bool throw_at_missing_includes {true};
bool escape_strings {};
};

} // namespace inja
Expand Down Expand Up @@ -2150,7 +2151,20 @@ class Renderer : public NodeVisitor {

void print_data(const std::shared_ptr<json> value) {
if (value->is_string()) {
*output_stream << value->get_ref<const json::string_t&>();
std::string val;
if (config.escape_strings) {
// get the value as a dump() to properly escape values
val = value->dump();

// strip the leading and trailing " characters that are added by dump()
// if C++20 is adopted, val.starts_with and val.ends_with would clean this up a bit
val = val.substr(0,1) == "\"" && val.substr(val.length()-1,1) == "\""
? val.substr(1, val.length()-2)
: val;
} else {
val = value->get_ref<const json::string_t&>();
}
*output_stream << val;
} else if (value->is_number_integer()) {
*output_stream << value->get<const json::number_integer_t>();
} else if (value->is_null()) {
Expand Down Expand Up @@ -2803,6 +2817,11 @@ class Environment {
lexer_config.notation = notation;
}

/// Sets the config for rendering strings raw or escaped
void set_escape_strings(bool escape_strings) {
render_config.escape_strings = escape_strings;
}

/// Sets the element notation syntax
void set_search_included_templates_in_files(bool search_in_files) {
parser_config.search_included_templates_in_files = search_in_files;
Expand Down
6 changes: 6 additions & 0 deletions test/test-renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ TEST_CASE("types") {
data["relatives"]["brother"] = "Chris";
data["relatives"]["sister"] = "Jenny";
data["vars"] = {2, 3, 4, 0, -1, -2, -3};
data["quoted"] = "\"quoted value\"";
data["json_pointers"]["example.com"] = "online";
data["json_pointers"]["and/or"] = "slash";
data["json_pointers"]["and~or"] = "tilde";
Expand All @@ -42,6 +43,11 @@ TEST_CASE("types") {
CHECK(env.render("{{ @name }}", data) == "@name");
CHECK(env.render("{{ $name }}", data) == "$name");

CHECK(env.render("{\"Value\":\"{{ quoted }}\"}", data) == "{\"Value\":\"\"quoted value\"\"}");
env.set_escape_strings(true);
CHECK(env.render("{\"Value\":\"{{ quoted }}\"}", data) == "{\"Value\":\"\\\"quoted value\\\"\"}");
env.set_escape_strings(false);

env.set_element_notation(inja::ElementNotation::Pointer);
CHECK(env.render("{{ json_pointers/example.com }}", data) == "online");
CHECK(env.render("{{ json_pointers/and~1or }}", data) == "slash");
Expand Down

0 comments on commit 2c441a3

Please sign in to comment.