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  std::string result(s.size() + space, '\\');
98  std::size_t pos = 0;
99 
100  for (const auto& c : s)
101  {
102  switch (c)
103  {
104  // quotation mark (0x22)
105  case '"':
106  {
107  result[pos + 1] = '"';
108  pos += 2;
109  break;
110  }
111 
112 
113  // reverse solidus (0x5c)
114  case '\\':
115  {
116  // nothing to change
117  pos += 2;
118  break;
119  }
120 
121 
122  // backspace (0x08)
123  case '\b':
124  {
125  result[pos + 1] = 'b';
126  pos += 2;
127  break;
128  }
129 
130 
131  // formfeed (0x0c)
132  case '\f':
133  {
134  result[pos + 1] = 'f';
135  pos += 2;
136  break;
137  }
138 
139 
140  // newline (0x0a)
141  case '\n':
142  {
143  result[pos + 1] = 'n';
144  pos += 2;
145  break;
146  }
147 
148 
149  // carriage return (0x0d)
150  case '\r':
151  {
152  result[pos + 1] = 'r';
153  pos += 2;
154  break;
155  }
156 
157 
158  // horizontal tab (0x09)
159  case '\t':
160  {
161  result[pos + 1] = 't';
162  pos += 2;
163  break;
164  }
165 
166 
167  default:
168  {
169  if (c >= 0x00 && c <= 0x1f)
170  {
171  // print character c as \uxxxx
172  sprintf(&result[pos + 1], "u%04x", int(c));
173  pos += 6;
174  // overwrite trailing null character
175  result[pos] = '\\';
176  }
177  else
178  {
179  // all other characters are added as-is
180  result[pos++] = c;
181  }
182  break;
183  }
184  }
185  }
186 
187  return result;
188  }
189  }
190 
198  CSV_INLINE std::string CSVRow::to_json(const std::vector<std::string>& subset) const {
199  std::vector<std::string> col_names = subset;
200  if (subset.empty()) {
201  col_names = this->data ? this->get_col_names() : std::vector<std::string>({});
202  }
203 
204  const size_t _n_cols = col_names.size();
205  std::string ret = "{";
206 
207  for (size_t i = 0; i < _n_cols; i++) {
208  auto& col = col_names[i];
209  auto field = this->operator[](col);
210 
211  // TODO: Possible performance enhancements by caching escaped column names
212  ret += '"' + internals::json_escape_string(col) + "\":";
213 
214  // Add quotes around strings but not numbers
215  if (field.is_num())
216  ret += internals::json_escape_string(field.get<csv::string_view>());
217  else
218  ret += '"' + internals::json_escape_string(field.get<csv::string_view>()) + '"';
219 
220  // Do not add comma after last string
221  if (i + 1 < _n_cols)
222  ret += ',';
223  }
224 
225  ret += '}';
226  return ret;
227  }
228 
236  CSV_INLINE std::string CSVRow::to_json_array(const std::vector<std::string>& subset) const {
237  std::vector<std::string> col_names = subset;
238  if (subset.empty())
239  col_names = this->data ? this->get_col_names() : std::vector<std::string>({});
240 
241  const size_t _n_cols = col_names.size();
242  std::string ret = "[";
243 
244  for (size_t i = 0; i < _n_cols; i++) {
245  auto field = this->operator[](col_names[i]);
246 
247  // Add quotes around strings but not numbers
248  if (field.is_num())
249  ret += internals::json_escape_string(field.get<csv::string_view>());
250  else
251  ret += '"' + internals::json_escape_string(field.get<csv::string_view>()) + '"';
252 
253  // Do not add comma after last string
254  if (i + 1 < _n_cols)
255  ret += ',';
256  }
257 
258  ret += ']';
259  return ret;
260  }
261 }
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:321
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