-
Notifications
You must be signed in to change notification settings - Fork 80
/
json-uri.cpp
159 lines (122 loc) · 3.47 KB
/
json-uri.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
* JSON schema validator for JSON for modern C++
*
* Copyright (c) 2016-2019 Patrick Boettcher <[email protected]>.
*
* SPDX-License-Identifier: MIT
*
*/
#include <nlohmann/json-schema.hpp>
#include <sstream>
namespace nlohmann
{
void json_uri::update(const std::string &uri)
{
std::string pointer = ""; // default pointer is document-root
// first split the URI into location and pointer
auto pointer_separator = uri.find('#');
if (pointer_separator != std::string::npos) { // and extract the pointer-string if found
pointer = uri.substr(pointer_separator + 1); // remove #
// unescape %-values IOW, decode JSON-URI-formatted JSON-pointer
std::size_t pos = pointer.size() - 1;
do {
pos = pointer.rfind('%', pos);
if (pos == std::string::npos)
break;
if (pos >= pointer.size() - 2) {
pos--;
continue;
}
std::string hex = pointer.substr(pos + 1, 2);
char ascii = static_cast<char>(std::strtoul(hex.c_str(), nullptr, 16));
pointer.replace(pos, 3, 1, ascii);
pos--;
} while (1);
}
auto location = uri.substr(0, pointer_separator);
if (location.size()) { // a location part has been found
// if it is an URN take it as it is
if (location.find("urn:") == 0) {
urn_ = location;
// and clear URL members
scheme_ = "";
authority_ = "";
path_ = "";
} else { // it is an URL
// split URL in protocol, hostname and path
std::size_t pos = 0;
auto proto = location.find("://", pos);
if (proto != std::string::npos) { // extract the protocol
urn_ = ""; // clear URN-member if URL is parsed
scheme_ = location.substr(pos, proto - pos);
pos = 3 + proto; // 3 == "://"
auto authority = location.find("/", pos);
if (authority != std::string::npos) { // and the hostname (no proto without hostname)
authority_ = location.substr(pos, authority - pos);
pos = authority;
}
}
auto path = location.substr(pos);
// URNs cannot of have paths
if (urn_.size() && path.size())
throw std::invalid_argument("Cannot add a path (" + path + ") to an URN URI (" + urn_ + ")");
if (path[0] == '/') // if it starts with a / it is root-path
path_ = path;
else if (pos == 0) { // the URL contained only a path and the current path has no / at the end, strip last element until / and append
auto last_slash = path_.rfind('/');
path_ = path_.substr(0, last_slash) + '/' + path;
} else // otherwise it is a subfolder
path_.append(path);
}
}
pointer_ = ""_json_pointer;
identifier_ = "";
if (pointer[0] == '/')
pointer_ = json::json_pointer(pointer);
else
identifier_ = pointer;
}
std::string json_uri::location() const
{
if (urn_.size())
return urn_;
std::stringstream s;
if (scheme_.size() > 0)
s << scheme_ << "://";
s << authority_
<< path_;
return s.str();
}
std::string json_uri::to_string() const
{
std::stringstream s;
s << location() << " # ";
if (identifier_ == "")
s << pointer_.to_string();
else
s << identifier_;
return s.str();
}
std::ostream &operator<<(std::ostream &os, const json_uri &u)
{
return os << u.to_string();
}
std::string json_uri::escape(const std::string &src)
{
std::vector<std::pair<std::string, std::string>> chars = {
{"~", "~0"},
{"/", "~1"}};
std::string l = src;
for (const auto &c : chars) {
std::size_t pos = 0;
do {
pos = l.find(c.first, pos);
if (pos == std::string::npos)
break;
l.replace(pos, 1, c.second);
pos += c.second.size();
} while (1);
}
return l;
}
} // namespace nlohmann