Skip to content

Commit

Permalink
Fixed issue #255
Browse files Browse the repository at this point in the history
  • Loading branch information
danielaparker committed Jul 2, 2020
1 parent 2032de0 commit 3adaee8
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 6 deletions.
151 changes: 146 additions & 5 deletions include/jsoncons_ext/jsonpath/flatten.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,62 @@

namespace jsoncons { namespace jsonpath {

template <class CharT, class Sink>
std::size_t escape_string(const CharT* s, std::size_t length,
Sink& sink)
{
std::size_t count = 0;
const CharT* begin = s;
const CharT* end = s + length;
for (const CharT* it = begin; it != end; ++it)
{
CharT c = *it;
switch (c)
{
case '\\':
sink.push_back('\\');
sink.push_back('\\');
count += 2;
break;
case '\'':
sink.push_back('\\');
sink.push_back('\'');
count += 2;
break;
case '\b':
sink.push_back('\\');
sink.push_back('b');
count += 2;
break;
case '\f':
sink.push_back('\\');
sink.push_back('f');
count += 2;
break;
case '\n':
sink.push_back('\\');
sink.push_back('n');
count += 2;
break;
case '\r':
sink.push_back('\\');
sink.push_back('r');
count += 2;
break;
case '\t':
sink.push_back('\\');
sink.push_back('t');
count += 2;
break;
default:
sink.push_back(c);
++count;
break;
}
}
return count;
}

template<class Json>
void flatten_(const std::basic_string<typename Json::char_type>& parent_key,
const Json& parent_value,
Expand Down Expand Up @@ -60,11 +116,10 @@ namespace jsoncons { namespace jsonpath {
for (const auto& item : parent_value.object_range())
{
string_type key(parent_key);
bool no_single_quote = item.key().find('\'') == string_type::npos;
key.push_back('[');
key.push_back(no_single_quote ? '\'' : '\"');
std::copy(item.key().begin(),item.key().end(), std::back_inserter(key));
key.push_back(no_single_quote ? '\'' : '\"');
key.push_back('\'');
escape_string(item.key().data(), item.key().length(), key);
key.push_back('\'');
key.push_back(']');
flatten_(key, item.value(), result);
}
Expand Down Expand Up @@ -97,7 +152,9 @@ namespace jsoncons { namespace jsonpath {
single_quoted_name_state,
double_quoted_name_state,
index_state,
expect_right_bracket
expect_right_bracket,
double_quoted_string_escape_char,
single_quoted_string_escape_char
};

template<class Json>
Expand Down Expand Up @@ -189,6 +246,9 @@ namespace jsoncons { namespace jsonpath {
buffer.clear();
state = unflatten_state::expect_right_bracket;
break;
case '\\':
state = unflatten_state::single_quoted_string_escape_char;
break;
default:
buffer.push_back(*it);
break;
Expand All @@ -213,12 +273,93 @@ namespace jsoncons { namespace jsonpath {
buffer.clear();
state = unflatten_state::expect_right_bracket;
break;
case '\\':
state = unflatten_state::double_quoted_string_escape_char;
break;
default:
buffer.push_back(*it);
break;
}
break;
}
case unflatten_state::double_quoted_string_escape_char:
switch (*it)
{
case '\"':
buffer.push_back('\"');
state = unflatten_state::double_quoted_name_state;
break;
case '\\':
buffer.push_back('\\');
state = unflatten_state::double_quoted_name_state;
break;
case '/':
buffer.push_back('/');
state = unflatten_state::double_quoted_name_state;
break;
case 'b':
buffer.push_back('\b');
state = unflatten_state::double_quoted_name_state;
break;
case 'f':
buffer.push_back('\f');
state = unflatten_state::double_quoted_name_state;
break;
case 'n':
buffer.push_back('\n');
state = unflatten_state::double_quoted_name_state;
break;
case 'r':
buffer.push_back('\r');
state = unflatten_state::double_quoted_name_state;
break;
case 't':
buffer.push_back('\t');
state = unflatten_state::double_quoted_name_state;
break;
default:
break;
}
break;
case unflatten_state::single_quoted_string_escape_char:
switch (*it)
{
case '\'':
buffer.push_back('\'');
state = unflatten_state::single_quoted_name_state;
break;
case '\\':
buffer.push_back('\\');
state = unflatten_state::double_quoted_name_state;
break;
case '/':
buffer.push_back('/');
state = unflatten_state::double_quoted_name_state;
break;
case 'b':
buffer.push_back('\b');
state = unflatten_state::double_quoted_name_state;
break;
case 'f':
buffer.push_back('\f');
state = unflatten_state::double_quoted_name_state;
break;
case 'n':
buffer.push_back('\n');
state = unflatten_state::double_quoted_name_state;
break;
case 'r':
buffer.push_back('\r');
state = unflatten_state::double_quoted_name_state;
break;
case 't':
buffer.push_back('\t');
state = unflatten_state::double_quoted_name_state;
break;
default:
break;
}
break;
case unflatten_state::index_state:
{
switch (*it)
Expand Down
49 changes: 48 additions & 1 deletion tests/src/jsonpath/jsonpath_flatten_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,55 @@ TEST_CASE("jsonpath flatten with single quote test")
json result = jsonpath::flatten(input);

json original = jsonpath::unflatten(result);
//std::cout << pretty_print(original) << "\n";
CHECK(original == input);
}
}

namespace {

void compare_match(jsoncons::json& doc,
const std::string& path,
const std::string& value)
{
auto result = jsoncons::jsonpath::json_query(doc, path);
CHECK_FALSE(result.empty()); // must match
CHECK_FALSE(result.size() > 1); // too many matches

auto matched_value = result[0].as<std::string>();
CHECK(value == matched_value);
}

void compare_paths(jsoncons::json& flat_doc, jsoncons::json& doc)
{
for (const auto& member : flat_doc.object_range())
{
const auto& path = member.key();
const auto& value = member.value().as<std::string>();
compare_match(doc, path, value);
}
}

} // namespace

TEST_CASE("jsonpath flatten escape")
{
std::string json
{
R"({)"
R"("data":)"
R"({)"
R"("a\"bc": "abc",)"
R"("d'ef": "def",)"
R"("g.hi": "ghi",)"
R"("j\\kl": "jkl",)"
R"("m/no": "mno",)"
R"("x\"y'z": "xyz")"
R"(})"
R"(})" };

jsoncons::json doc = jsoncons::json::parse(json);

auto flat_doc = jsoncons::jsonpath::flatten(doc);

compare_paths(flat_doc, doc);
}

0 comments on commit 3adaee8

Please sign in to comment.