Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions EXPRESSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ token, filename.
- [`indexOf`](#indexof)
- [`lastIndexOf`](#lastindexof)

### Encode/Decode Functions

- [`escapeJson`](#escapejson)
- [`unescapeJson`](#unescapejson)

## Planned Features

### String Manipulation
Expand Down Expand Up @@ -216,12 +221,10 @@ token, filename.

### Encode/Decode Functions

- `escapeJson`
- `escapeXml`
- `escapeCsv`
- `escapeHtml3`
- `escapeHtml4`
- `unescapeJson`
- `unescapeXml`
- `unescapeCsv`
- `unescapeHtml3`
Expand Down Expand Up @@ -954,3 +957,36 @@ following Expressions will provide the following results:
| `${filename:lastIndexOf('.')}` | `20` |
| `${filename:lastIndexOf('a')}` | `17` |
| `${filename:lastIndexOf(' ')}` | `11` |

### escapeJson

**Description**: This function prepares the Subject to be inserted into JSON
document by escaping the characters in the String using Json String rules. The
function correctly escapes quotes and control-chars (tab, backslash, cr, ff,
etc.)

**Subject Type**: String

**Arguments**: No arguments

**Return Type**: String

**Examples**:

If the "message" attribute is 'This is a "test!"', then the Expression
`${message:escapeJson()}` will return 'This is a \"test!\"'

### unescapeJson

**Description**: This function unescapes any Json literals found in the String.

**Subject Type**: String

**Arguments**: No arguments

**Return Type**: String

**Examples**:

If the "message" attribute is 'This is a \"test!\"', then the Expression
`${message:unescapeJson()}` will return 'This is a "test!"'
31 changes: 31 additions & 0 deletions extensions/expression-language/Expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
#include <iomanip>
#include <random>

#include "rapidjson/reader.h"
#include "rapidjson/writer.h"
#include "rapidjson/document.h"

#include <expression/Expression.h>
#include <regex>
#include "Driver.h"
Expand Down Expand Up @@ -170,6 +174,29 @@ Value expr_lastIndexOf(const std::vector<Value> &args) {
}
}

Value expr_escapeJson(const std::vector<Value> &args) {
const std::string &arg_0 = args[0].asString();
rapidjson::StringBuffer buf;
rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
writer.String(arg_0.c_str());
std::string result(buf.GetString());
return Value(result.substr(1, result.length()-2));
}

Value expr_unescapeJson(const std::vector<Value> &args) {
std::stringstream arg_0_ss;
arg_0_ss << "\"" << args[0].asString() << "\"";
rapidjson::Reader reader;
rapidjson::StringStream ss(arg_0_ss.str().c_str());
rapidjson::Document doc;
doc.ParseStream(ss);
if (doc.IsString()) {
return Value(std::string(doc.GetString()));
} else {
return Value();
}
}

#ifdef EXPRESSION_LANGUAGE_USE_REGEX

Value expr_replace(const std::vector<Value> &args) {
Expand Down Expand Up @@ -400,6 +427,10 @@ Expression make_dynamic_function(const std::string &function_name,
return make_dynamic_function_incomplete<expr_indexOf>(function_name, args, 1);
} else if (function_name == "lastIndexOf") {
return make_dynamic_function_incomplete<expr_lastIndexOf>(function_name, args, 1);
} else if (function_name == "escapeJson") {
return make_dynamic_function_incomplete<expr_escapeJson>(function_name, args, 0);
} else if (function_name == "unescapeJson") {
return make_dynamic_function_incomplete<expr_unescapeJson>(function_name, args, 0);
#ifdef EXPRESSION_LANGUAGE_USE_REGEX
} else if (function_name == "replace") {
return make_dynamic_function_incomplete<expr_replace>(function_name, args, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -744,3 +744,27 @@ TEST_CASE("Chained call 3", "[expressionChainedCall3]") { // NOLINT
flow_file_a->addAttribute("attr", "7");
REQUIRE("238" == expr({flow_file_a}).asString());
}

TEST_CASE("Encode JSON", "[expressionEncodeJSON]") { // NOLINT
auto expr = expression::compile("${message:escapeJson()}");

auto flow_file_a = std::make_shared<MockFlowFile>();
flow_file_a->addAttribute("message", "This is a \"test!\"");
REQUIRE("This is a \\\"test!\\\"" == expr({flow_file_a}).asString());
}

TEST_CASE("Decode JSON", "[expressionDecodeJSON]") { // NOLINT
auto expr = expression::compile("${message:unescapeJson()}");

auto flow_file_a = std::make_shared<MockFlowFile>();
flow_file_a->addAttribute("message", "This is a \\\"test!\\\"");
REQUIRE("This is a \"test!\"" == expr({flow_file_a}).asString());
}

TEST_CASE("Encode Decode JSON", "[expressionEncodeDecodeJSON]") { // NOLINT
auto expr = expression::compile("${message:escapeJson():unescapeJson()}");

auto flow_file_a = std::make_shared<MockFlowFile>();
flow_file_a->addAttribute("message", "This is a \"test!\"");
REQUIRE("This is a \"test!\"" == expr({flow_file_a}).asString());
}