Skip to content

Commit b9522e8

Browse files
committed
improving json_ptr
1 parent b3e1e4f commit b9522e8

File tree

4 files changed

+114
-93
lines changed

4 files changed

+114
-93
lines changed

lib/include/cpp-json/json_error.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ namespace json {
99
// general error
1010
class exception {
1111
public:
12-
size_t line = static_cast<size_t>(-1);
13-
size_t column = static_cast<size_t>(-1);
12+
size_t line = static_cast<size_t>(0);
13+
size_t column = static_cast<size_t>(0);
1414
};
1515

1616
// parsing errors
@@ -35,7 +35,7 @@ class invalid_index : public exception {};
3535

3636
// pointer errors
3737
class invalid_path : public exception {};
38-
class invalid_reference_escape : public exception {};
38+
class invalid_pointer_syntax : public exception {};
3939
}
4040

4141
#endif

lib/include/cpp-json/json_ptr.h

Lines changed: 44 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -36,104 +36,58 @@ class ptr {
3636
public:
3737
explicit ptr(std::string_view path) {
3838

39-
auto it = path.begin();
39+
if (path.empty() || path == "#") {
40+
return;
41+
}
4042

41-
bool uri_format = false;
43+
Reader reader(path);
4244

43-
if (it != path.end()) {
45+
// normal or URI fragment notation?
46+
const bool uri_format = reader.match('#');
4447

45-
// normal or URI fragment notation?
46-
if (*it == '#') {
47-
++it;
48-
uri_format = true;
49-
}
48+
if (!reader.match('/')) {
49+
throw invalid_pointer_syntax();
50+
}
5051

51-
while (it != path.end()) {
52-
if (*it++ != '/') {
53-
throw invalid_path();
52+
std::string reference_token;
53+
while (!reader.eof()) {
54+
55+
if (!uri_format) {
56+
if (reader.match("~0")) {
57+
reference_token.push_back('~');
58+
} else if (reader.match("~1")) {
59+
reference_token.push_back('/');
60+
} else if (reader.match("/")) {
61+
path_.push_back(reference_token);
62+
reference_token.clear();
63+
} else if (reader.match("~")) {
64+
throw invalid_pointer_syntax();
65+
} else {
66+
reference_token.push_back(reader.read());
5467
}
55-
56-
std::string reference_token;
57-
while (it != path.end() && *it != '/') {
58-
char ch = *it;
59-
60-
if (!uri_format) {
61-
if (ch == '~') {
62-
63-
// ~1 -> /
64-
// ~0 -> ~
65-
66-
++it;
67-
if (it == path.end()) {
68-
throw invalid_reference_escape();
69-
}
70-
71-
switch (*it) {
72-
case '0':
73-
ch = '~';
74-
break;
75-
case '1':
76-
ch = '/';
77-
break;
78-
default:
79-
throw invalid_reference_escape();
80-
}
81-
}
82-
} else {
83-
// %XX -> char(0xXX)
84-
85-
if (ch == '%') {
86-
++it;
87-
if (it == path.end()) {
88-
throw invalid_reference_escape();
89-
}
90-
91-
char hex[2];
92-
if (!isxdigit(*it)) {
93-
throw invalid_reference_escape();
94-
}
95-
96-
hex[0] = *it++;
97-
if (it == path.end()) {
98-
throw invalid_reference_escape();
99-
}
100-
101-
if (!isxdigit(*it)) {
102-
throw invalid_reference_escape();
103-
}
104-
105-
hex[1] = *it;
106-
107-
ch = static_cast<char>((detail::to_hex(hex[0]) << 4) | (detail::to_hex(hex[1])));
108-
} else if (ch == '~') {
109-
// ~1 -> /
110-
// ~0 -> ~
111-
112-
++it;
113-
if (it == path.end()) {
114-
throw invalid_reference_escape();
115-
}
116-
117-
switch (*it) {
118-
case '0':
119-
ch = '~';
120-
break;
121-
case '1':
122-
ch = '/';
123-
break;
124-
default:
125-
throw invalid_reference_escape();
126-
}
127-
}
128-
}
129-
130-
reference_token.push_back(ch);
131-
++it;
68+
} else {
69+
static const std::regex hex_regex(R"(%[0-9A-Fa-f]{2})");
70+
if (reader.match("~0")) {
71+
reference_token.push_back('~');
72+
} else if (reader.match("~1")) {
73+
reference_token.push_back('/');
74+
} else if (reader.match("/")) {
75+
path_.push_back(reference_token);
76+
reference_token.clear();
77+
} else if (reader.match("~")) {
78+
throw invalid_pointer_syntax();
79+
} else if (auto hex_value = reader.match(hex_regex)) {
80+
// %XX -> char(0xXX)
81+
reference_token.push_back(static_cast<char>((detail::to_hex(hex_value->data()[1]) << 4) | (detail::to_hex(hex_value->data()[2]))));
82+
} else if (reader.match("%")) {
83+
throw invalid_pointer_syntax();
84+
} else {
85+
reference_token.push_back(reader.read());
13286
}
133-
134-
path_.push_back(reference_token);
13587
}
13688
}
89+
90+
path_.push_back(reference_token);
13791
}
13892

13993
public:

test/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,22 @@ cmake_minimum_required(VERSION 3.15)
33
add_executable(example1 example1.cpp)
44
add_executable(example2 example2.cpp)
55
add_executable(example3 example3.cpp)
6+
add_executable(example4 example4.cpp)
67

78
target_link_libraries(example1 PUBLIC cpp-json)
89
target_link_libraries(example2 PUBLIC cpp-json)
910
target_link_libraries(example3 PUBLIC cpp-json)
11+
target_link_libraries(example4 PUBLIC cpp-json)
1012

1113
set_property(TARGET example1 PROPERTY CXX_STANDARD 17)
1214
set_property(TARGET example2 PROPERTY CXX_STANDARD 17)
1315
set_property(TARGET example3 PROPERTY CXX_STANDARD 17)
16+
set_property(TARGET example4 PROPERTY CXX_STANDARD 17)
1417

1518
target_compile_options(example1 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
1619
target_compile_options(example2 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
1720
target_compile_options(example3 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
21+
target_compile_options(example4 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow)
1822

1923
add_test(
2024
NAME example1
@@ -31,3 +35,8 @@ add_test(
3135
NAME example3
3236
COMMAND $<TARGET_FILE:example3>
3337
)
38+
39+
add_test(
40+
NAME example4
41+
COMMAND $<TARGET_FILE:example4>
42+
)

test/example4.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
#include "cpp-json/json.h"
3+
#include <iostream>
4+
5+
/**
6+
* @brief main
7+
* @return
8+
*/
9+
int main() {
10+
11+
try {
12+
// construct from string (C++11 raw string literals work nicely here!)
13+
auto v = json::parse(R"({
14+
"foo": ["bar", "baz"],
15+
"": 0,
16+
"a/b": 1,
17+
"c%d": 2,
18+
"e^f": 3,
19+
"g|h": 4,
20+
"i\\j": 5,
21+
"k\"l": 6,
22+
" ": 7,
23+
"m~n": 8
24+
})");
25+
26+
std::cout << json::stringify(v[json::ptr("")]) << std::endl;
27+
std::cout << json::stringify(v[json::ptr("/foo")]) << std::endl;
28+
std::cout << json::stringify(v[json::ptr("/foo/0")]) << std::endl;
29+
std::cout << json::stringify(v[json::ptr("/")]) << std::endl;
30+
std::cout << json::stringify(v[json::ptr("/a~1b")]) << std::endl;
31+
std::cout << json::stringify(v[json::ptr("/c%d")]) << std::endl;
32+
std::cout << json::stringify(v[json::ptr("/e^f")]) << std::endl;
33+
std::cout << json::stringify(v[json::ptr("/g|h")]) << std::endl;
34+
std::cout << json::stringify(v[json::ptr("/i\\j")]) << std::endl;
35+
std::cout << json::stringify(v[json::ptr("/k\"l")]) << std::endl;
36+
std::cout << json::stringify(v[json::ptr("/ ")]) << std::endl;
37+
std::cout << json::stringify(v[json::ptr("/m~0n")]) << std::endl;
38+
39+
std::cout << "-----------------" << std::endl;
40+
41+
std::cout << json::stringify(v[json::ptr("#")]) << std::endl;
42+
std::cout << json::stringify(v[json::ptr("#/foo")]) << std::endl;
43+
std::cout << json::stringify(v[json::ptr("#/foo/0")]) << std::endl;
44+
std::cout << json::stringify(v[json::ptr("#/")]) << std::endl;
45+
std::cout << json::stringify(v[json::ptr("#/a~1b")]) << std::endl;
46+
std::cout << json::stringify(v[json::ptr("#/c%25d")]) << std::endl;
47+
std::cout << json::stringify(v[json::ptr("#/e%5Ef")]) << std::endl;
48+
std::cout << json::stringify(v[json::ptr("#/g%7ch")]) << std::endl;
49+
std::cout << json::stringify(v[json::ptr("#/i%5Cj")]) << std::endl;
50+
std::cout << json::stringify(v[json::ptr("#/k%22l")]) << std::endl;
51+
std::cout << json::stringify(v[json::ptr("#/%20")]) << std::endl;
52+
std::cout << json::stringify(v[json::ptr("#/m~0n")]) << std::endl;
53+
54+
} catch (const json::exception &e) {
55+
std::cerr << "Error on line: " << e.line << ", column: " << e.column << std::endl;
56+
throw;
57+
}
58+
}

0 commit comments

Comments
 (0)