Vince's CSV Parser
csv_row_json.cpp
Go to the documentation of this file.
1 
5 #include "csv_row.hpp"
6 
7 namespace csv {
8  /*
9  The implementations for json_extra_space() and json_escape_string()
10  were modified from source code for JSON for Modern C++.
11 
12  The respective license is below:
13 
14  The code is licensed under the [MIT
15  License](http://opensource.org/licenses/MIT):
16 
17  Copyright © 2013-2015 Niels Lohmann.
18 
19  Permission is hereby granted, free of charge, to any person
20  obtaining a copy of this software and associated documentation files
21  (the "Software"), to deal in the Software without restriction,
22  including without limitation the rights to use, copy, modify, merge,
23  publish, distribute, sublicense, and/or sell copies of the Software,
24  and to permit persons to whom the Software is furnished to do so,
25  subject to the following conditions:
26 
27  The above copyright notice and this permission notice shall be
28  included in all copies or substantial portions of the Software.
29 
30  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
31  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
33  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
34  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
35  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
36  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37  SOFTWARE.
38  */
39 
40  namespace internals {
49  static std::size_t json_extra_space(csv::string_view& s) noexcept
50  {
51  std::size_t result = 0;
52 
53 
54  for (const auto& c : s)
55  {
56  switch (c)
57  {
58  case '"':
59  case '\\':
60  case '\b':
61  case '\f':
62  case '\n':
63  case '\r':
64  case '\t':
65  {
66  // from c (1 byte) to \x (2 bytes)
67  result += 1;
68  break;
69  }
70 
71 
72  default:
73  {
74  if (c >= 0x00 && c <= 0x1f)
75  {
76  // from c (1 byte) to \uxxxx (6 bytes)
77  result += 5;
78  }
79  break;
80  }
81  }
82  }
83 
84 
85  return result;
86  }
87 
88  CSV_INLINE std::string json_escape_string(csv::string_view s) noexcept
89  {
90  const auto space = json_extra_space(s);
91  if (space == 0)
92  {
93  return std::string(s);
94  }
95 
96  // create a result string of necessary size
97  size_t result_size = s.size() + space;
98  std::string result(result_size, '\\');
99  std::size_t pos = 0;
100 
101  for (const auto& c : s)
102  {
103  switch (c)
104  {
105  // quotation mark (0x22)
106  case '"':
107  {
108  result[pos + 1] = '"';
109  pos += 2;
110  break;
111  }
112 
113 
114  // reverse solidus (0x5c)
115  case '\\':
116  {
117  // nothing to change
118  pos += 2;
119  break;
120  }
121 
122 
123  // backspace (0x08)
124  case '\b':
125  {
126  result[pos + 1] = 'b';
127  pos += 2;
128  break;
129  }
130 
131 
132  // formfeed (0x0c)
133  case '\f':
134  {
135  result[pos + 1] = 'f';
136  pos += 2;
137  break;
138  }
139 
140 
141  // newline (0x0a)
142  case '\n':
143  {
144  result[pos + 1] = 'n';
145  pos += 2;
146  break;
147  }
148 
149 
150  // carriage return (0x0d)
151  case '\r':
152  {
153  result[pos + 1] = 'r';
154  pos += 2;
155  break;
156  }
157 
158 
159  // horizontal tab (0x09)
160  case '\t':
161  {
162  result[pos + 1] = 't';
163  pos += 2;
164  break;
165  }
166 
167 
168  default:
169  {
170  if (c >= 0x00 && c <= 0x1f)
171  {
172  // print character c as \uxxxx
173  snprintf(&result[pos + 1], result_size - pos - 1, "u%04x", int(c));
174  pos += 6;
175  // overwrite trailing null character
176  result[pos] = '\\';
177  }
178  else
179  {
180  // all other characters are added as-is
181  result[pos++] = c;
182  }
183  break;
184  }
185  }
186  }
187 
188  return result;
189  }
190  }
191 
199  CSV_INLINE std::string CSVRow::to_json(const std::vector<std::string>& subset) const {
200  std::vector<std::string> col_names = subset;
201  if (subset.empty()) {
202  col_names = this->data ? this->get_col_names() : std::vector<std::string>({});
203  }
204 
205  const size_t _n_cols = col_names.size();
206  std::string ret = "{";
207 
208  for (size_t i = 0; i < _n_cols; i++) {
209  auto& col = col_names[i];
210  auto field = this->operator[](col);
211 
212  // TODO: Possible performance enhancements by caching escaped column names
213  ret += '"' + internals::json_escape_string(col) + "\":";
214 
215  // Add quotes around strings but not numbers
216  if (field.is_num())
217  ret += internals::json_escape_string(field.get<csv::string_view>());
218  else
219  ret += '"' + internals::json_escape_string(field.get<csv::string_view>()) + '"';
220 
221  // Do not add comma after last string
222  if (i + 1 < _n_cols)
223  ret += ',';
224  }
225 
226  ret += '}';
227  return ret;
228  }
229 
237  CSV_INLINE std::string CSVRow::to_json_array(const std::vector<std::string>& subset) const {
238  std::vector<std::string> col_names = subset;
239  if (subset.empty())
240  col_names = this->data ? this->get_col_names() : std::vector<std::string>({});
241 
242  const size_t _n_cols = col_names.size();
243  std::string ret = "[";
244 
245  for (size_t i = 0; i < _n_cols; i++) {
246  auto field = this->operator[](col_names[i]);
247 
248  // Add quotes around strings but not numbers
249  if (field.is_num())
250  ret += internals::json_escape_string(field.get<csv::string_view>());
251  else
252  ret += '"' + internals::json_escape_string(field.get<csv::string_view>()) + '"';
253 
254  // Do not add comma after last string
255  if (i + 1 < _n_cols)
256  ret += ',';
257  }
258 
259  ret += ']';
260  return ret;
261  }
262 }
std::string to_json(const std::vector< std::string > &subset={}) const
Convert a CSV row to a JSON object, i.e.
std::string to_json_array(const std::vector< std::string > &subset={}) const
Convert a CSV row to a JSON array, i.e.
std::vector< std::string > get_col_names() const
Retrieve this row's associated column names.
Definition: csv_row.hpp:329
CSVField operator[](size_t n) const
Return a CSVField object corrsponding to the nth value in the row.
Definition: csv_row.cpp:34
#define CSV_INLINE
Helper macro which should be #defined as "inline" in the single header version.
Definition: common.hpp:26
Defines the data type used for storing information about a CSV row.
The all encompassing namespace.
nonstd::string_view string_view
The string_view class used by this library.
Definition: common.hpp:75