View on GitHub
jbson
C++11/1y BSON library
json_writer.hpp
1 // Copyright Christian Manning 2013.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef JBSON_JSON_WRITER_HPP
7 #define JBSON_JSON_WRITER_HPP
8 
9 #include <string>
10 #include <iterator>
11 
12 #include "detail/config.hpp"
13 
14 JBSON_PUSH_DISABLE_DOCUMENTATION_WARNING
15 #include <boost/utility/string_ref.hpp>
16 #include <boost/range/as_literal.hpp>
17 #include <boost/concept_check.hpp>
18 JBSON_CLANG_POP_WARNINGS
19 
20 #include "element.hpp"
21 #include "document.hpp"
22 #include "builder.hpp"
23 #include "detail/visit.hpp"
24 
25 JBSON_PUSH_DISABLE_DEPRECATED_WARNING
26 
27 namespace jbson {
28 
29 struct json_writer;
30 
31 template <typename OutputIterator, typename Container>
32 std::decay_t<OutputIterator> write_json(const basic_array<Container>&, OutputIterator);
33 template <typename OutputIterator, typename Container>
34 std::decay_t<OutputIterator> write_json(const basic_document<Container>&, OutputIterator);
35 
36 namespace detail {
37 
38 template <element_type EType, typename Element, typename OutputIterator> struct json_element_visitor;
39 
40 namespace {
41 
42 template <typename T, typename OutputIterator>
43 std::decay_t<OutputIterator> stringify(T&& v, OutputIterator out,
44  std::enable_if_t<std::is_integral<std::decay_t<T>>::value>* = nullptr) {
45  if(std::is_same<T, bool>::value)
46  return boost::range::copy(boost::as_literal(!!v ? "true" : "false"), out);
47  std::array<char, std::numeric_limits<T>::digits10 + 2> int_str;
48  auto n = std::snprintf(int_str.data(), int_str.size(), "%zd", static_cast<ptrdiff_t>(v));
49  assert(n > 0);
50  assert(static_cast<size_t>(n) <= int_str.size());
51  return boost::range::copy(boost::string_ref{int_str.data(), static_cast<size_t>(n)}, out);
52 }
53 
54 template <typename T, typename OutputIterator>
55 std::decay_t<OutputIterator> stringify(T&& v, OutputIterator out,
56  std::enable_if_t<std::is_floating_point<std::decay_t<T>>::value>* = nullptr) {
57  std::array<char, std::numeric_limits<T>::digits10 + 2> float_str;
58  auto n = std::snprintf(float_str.data(), float_str.size(), "%.8g", v);
59  assert(n > 0);
60  assert(static_cast<size_t>(n) <= float_str.size());
61  out = boost::range::copy(boost::string_ref{float_str.data(), static_cast<size_t>(n)}, out);
62  return out;
63 }
64 
65 template <typename OutputIterator> std::decay_t<OutputIterator> stringify(boost::string_ref v, OutputIterator out) {
66  auto str = std::vector<char>(v.begin(), v.end());
67  for(auto i = str.begin(); i != str.end(); ++i) {
68  switch(*i) {
69  case '"':
70  i = ++str.insert(i, '\\');
71  break;
72  case '\\':
73  i = ++str.insert(i, '\\');
74  break;
75  case '/':
76  i = ++str.insert(i, '\\');
77  break;
78  case '\b':
79  i = ++str.insert(i, '\\');
80  *i = 'b';
81  break;
82  case '\f':
83  i = ++str.insert(i, '\\');
84  *i = 'f';
85  break;
86  case '\n':
87  i = ++str.insert(i, '\\');
88  *i = 'n';
89  break;
90  case '\r':
91  i = ++str.insert(i, '\\');
92  *i = 'r';
93  break;
94  case '\t':
95  i = ++str.insert(i, '\\');
96  *i = 't';
97  break;
98  default:
99  if(::iscntrl(*i)) {
100  std::array<char, 7> hex;
101  auto n = std::snprintf(hex.data(), hex.size(), "\\u%04x", std::char_traits<char>::to_int_type(*i));
102  assert(n == 6);
103  i = str.erase(i);
104  i = str.insert(i, hex.begin(), std::next(hex.begin(), n));
105  }
106  break;
107  }
108  }
109  *out++ = '"';
110  out = boost::range::copy(str, out);
111  *out++ = '"';
112  return out;
113 }
114 
115 template <typename C, typename EC, typename OutputIterator>
116 std::decay_t<OutputIterator> stringify(const basic_document<C, EC>& doc, OutputIterator out) {
117  out = boost::range::copy(boost::as_literal("{ "), out);
118 
119  auto end = doc.end();
120  for(auto it = doc.begin(); it != end;) {
121  out = stringify(it->name(), out);
122  out = boost::range::copy(boost::as_literal(" : "), out);
123  out = detail::visit<detail::json_element_visitor>(it->type(), *it, out);
124  if(++it != end)
125  out = boost::range::copy(boost::as_literal(", "), out);
126  }
127 
128  return boost::range::copy(boost::as_literal(" }"), out);
129 }
130 
131 template <typename C, typename EC, typename OutputIterator>
132 std::decay_t<OutputIterator> stringify(const basic_array<C, EC>& arr, OutputIterator out) {
133  out = boost::range::copy(boost::as_literal("[ "), out);
134 
135  auto end = arr.end();
136  for(auto it = arr.begin(); it != end;) {
137  out = detail::visit<detail::json_element_visitor>(it->type(), *it, out);
138  if(++it != end)
139  out = boost::range::copy(boost::as_literal(", "), out);
140  }
141 
142  return boost::range::copy(boost::as_literal(" ]"), out);
143 }
144 
145 } // namespace
146 
147 #ifndef DOXYGEN_SHOULD_SKIP_THIS
148 
149 namespace concepts {
150 #include <boost/concept/detail/concept_def.hpp>
151 BOOST_concept(OutputIterator, (TT)(ValueT)) {
152  BOOST_CONCEPT_USAGE(OutputIterator) {
153  ++i; // require preincrement operator
154  i++; // require postincrement operator
155  *i++ = t; // require postincrement and assignment
156  }
157 
158  private:
159  TT i{std::declval<TT>()}, j{std::declval<TT>()};
160  ValueT t = ValueT();
161 };
162 #include <boost/concept/detail/concept_undef.hpp>
163 } // namespace concepts
164 
165 #endif // DOXYGEN_SHOULD_SKIP_THIS
166 
167 template <element_type EType, typename Element, typename OutputIteratorT> struct json_element_visitor {
168  static_assert(detail::is_element<std::decay_t<Element>>::value, "");
169  BOOST_CONCEPT_ASSERT((concepts::OutputIteratorConcept<OutputIteratorT, char>));
170 
171  json_element_visitor() = default;
172 
173  std::decay_t<OutputIteratorT> operator()(Element&& e, OutputIteratorT out) const {
174  return stringify(get<EType>(e), out);
175  }
176 };
177 
178 #ifndef DOXYGEN_SHOULD_SKIP_THIS
179 
180 // oid
181 template <typename Element, typename OutputIterator>
182 struct json_element_visitor<element_type::oid_element, Element, OutputIterator> {
183  std::decay_t<OutputIterator> operator()(Element&& e, std::decay_t<OutputIterator> out) const {
184  auto oid = get<element_type::oid_element>(e);
185  std::array<char, 25> buf;
186  for(size_t i = 0; i < oid.size(); ++i)
187  std::snprintf(&buf[i * 2], 3, "%0.2x", static_cast<unsigned char>(oid[i]));
188  return stringify(
189  static_cast<document>(builder("$oid", element_type::string_element, boost::string_ref(buf.data(), 24))),
190  out);
191  }
192 };
193 
194 // db pointer
195 template <typename Element, typename OutputIterator>
196 struct json_element_visitor<element_type::db_pointer_element, Element, OutputIterator> {
197  std::decay_t<OutputIterator> operator()(Element&& e, std::decay_t<OutputIterator> out) const {
198  using string_type = decltype(get<element_type::string_element>(e));
199  string_type ref;
200  using oid_type = decltype(get<element_type::oid_element>(e));
201  oid_type oid;
202  std::tie(ref, oid) = get<element_type::db_pointer_element>(e);
203  return stringify(static_cast<document>(
204  builder("$ref", element_type::string_element, ref)("$id", element_type::oid_element, oid)),
205  out);
206  }
207 };
208 
209 // date
210 template <typename Element, typename OutputIterator>
211 struct json_element_visitor<element_type::date_element, Element, OutputIterator> {
212  std::decay_t<OutputIterator> operator()(Element&& e, std::decay_t<OutputIterator> out) const {
213  return stringify(static_cast<document>(builder("$date", e.template value<int64_t>())), out);
214  }
215 };
216 
217 // regex
218 template <typename Element, typename OutputIterator>
219 struct json_element_visitor<element_type::regex_element, Element, OutputIterator> {
220  std::decay_t<OutputIterator> operator()(Element&& e, std::decay_t<OutputIterator> out) const {
221  using string_type = decltype(get<element_type::string_element>(e));
222  string_type regex, options;
223  std::tie(regex, options) = get<element_type::regex_element>(e);
224  return stringify(static_cast<document>(builder("$regex", element_type::string_element,
225  regex)("$options", element_type::string_element, options)),
226  out);
227  }
228 };
229 
230 // scoped code
231 template <typename Element, typename OutputIterator>
232 struct json_element_visitor<element_type::scoped_javascript_element, Element, OutputIterator> {
233  std::decay_t<OutputIterator> operator()(Element&&, std::decay_t<OutputIterator> out) const { return out; }
234 };
235 
236 // voids
237 template <typename Element, typename OutputIterator>
238 struct json_element_visitor<element_type::null_element, Element, OutputIterator> {
239  std::decay_t<OutputIterator> operator()(Element&&, std::decay_t<OutputIterator> out) const {
240  return boost::range::copy(boost::as_literal("null"), out);
241  }
242 };
243 
244 template <typename Element, typename OutputIterator>
245 struct json_element_visitor<element_type::undefined_element, Element, OutputIterator> {
246  std::decay_t<OutputIterator> operator()(Element&&, std::decay_t<OutputIterator> out) const {
247  return boost::range::copy(boost::as_literal("null"), out);
248  }
249 };
250 
251 template <typename Element, typename OutputIterator>
252 struct json_element_visitor<element_type::max_key, Element, OutputIterator> {
253  std::decay_t<OutputIterator> operator()(Element&&, std::decay_t<OutputIterator> out) const {
254  return boost::range::copy(boost::as_literal("null"), out);
255  }
256 };
257 
258 template <typename Element, typename OutputIterator>
259 struct json_element_visitor<element_type::min_key, Element, OutputIterator> {
260  std::decay_t<OutputIterator> operator()(Element&&, std::decay_t<OutputIterator> out) const {
261  return boost::range::copy(boost::as_literal("null"), out);
262  }
263 };
264 
265 #endif // DOXYGEN_SHOULD_SKIP_THIS
266 
267 } // namespace detail
268 
269 template <typename OutputIterator, typename Container>
270 std::decay_t<OutputIterator> write_json(const basic_array<Container>& arr, OutputIterator out) {
271  return detail::stringify(arr, out);
272 }
273 
274 template <typename OutputIterator, typename Container>
275 std::decay_t<OutputIterator> write_json(const basic_document<Container>& doc, OutputIterator out) {
276  return detail::stringify(doc, out);
277 }
278 
279 } // namespace jbson
280 
281 JBSON_POP_WARNINGS
282 
283 #endif // JBSON_JSON_WRITER_HPP
element_type
The element_type enum represents a BSON data type.
Definition: element_fwd.hpp:36
std::string or boost::string_ref (string_type)