aboutsummaryrefslogtreecommitdiff
path: root/include/jsoncons_ext/csv
diff options
context:
space:
mode:
authorRichard <q@1bpm.net>2022-09-04 00:32:56 +0100
committerRichard <q@1bpm.net>2022-09-04 00:32:56 +0100
commit1d055261b4144dbf86b2658437015b15d4dd9bff (patch)
tree6049b19d1bf953a650383de1a5e438b8b82679f6 /include/jsoncons_ext/csv
downloadcsound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.gz
csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.bz2
csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.zip
initial
Diffstat (limited to 'include/jsoncons_ext/csv')
-rw-r--r--include/jsoncons_ext/csv/csv.hpp17
-rw-r--r--include/jsoncons_ext/csv/csv_cursor.hpp358
-rw-r--r--include/jsoncons_ext/csv/csv_encoder.hpp954
-rw-r--r--include/jsoncons_ext/csv/csv_error.hpp85
-rw-r--r--include/jsoncons_ext/csv/csv_options.hpp973
-rw-r--r--include/jsoncons_ext/csv/csv_parser.hpp2097
-rw-r--r--include/jsoncons_ext/csv/csv_reader.hpp348
-rw-r--r--include/jsoncons_ext/csv/csv_serializer.hpp12
-rw-r--r--include/jsoncons_ext/csv/decode_csv.hpp208
-rw-r--r--include/jsoncons_ext/csv/encode_csv.hpp122
10 files changed, 5174 insertions, 0 deletions
diff --git a/include/jsoncons_ext/csv/csv.hpp b/include/jsoncons_ext/csv/csv.hpp
new file mode 100644
index 0000000..9f8a9c5
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv.hpp
@@ -0,0 +1,17 @@
+/// Copyright 2013 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_CSV_HPP
+#define JSONCONS_CSV_CSV_HPP
+
+#include <jsoncons_ext/csv/csv_options.hpp>
+#include <jsoncons_ext/csv/csv_reader.hpp>
+#include <jsoncons_ext/csv/csv_encoder.hpp>
+#include <jsoncons_ext/csv/csv_cursor.hpp>
+#include <jsoncons_ext/csv/decode_csv.hpp>
+#include <jsoncons_ext/csv/encode_csv.hpp>
+
+#endif
diff --git a/include/jsoncons_ext/csv/csv_cursor.hpp b/include/jsoncons_ext/csv/csv_cursor.hpp
new file mode 100644
index 0000000..67d55a6
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_cursor.hpp
@@ -0,0 +1,358 @@
+// Copyright 2018 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_CSV_CURSOR_HPP
+#define JSONCONS_CSV_CSV_CURSOR_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <istream> // std::basic_istream
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons_ext/csv/csv_parser.hpp>
+#include <jsoncons/staj_cursor.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/source_adaptor.hpp>
+
+namespace jsoncons { namespace csv {
+
+template<class CharT,class Source=jsoncons::stream_source<CharT>,class Allocator=std::allocator<char>>
+class basic_csv_cursor : public basic_staj_cursor<CharT>, private virtual ser_context
+{
+public:
+ using source_type = Source;
+ using char_type = CharT;
+ using allocator_type = Allocator;
+private:
+ static constexpr size_t default_max_buffer_size = 16384;
+
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<CharT> char_allocator_type;
+
+ text_source_adaptor<Source> source_;
+ basic_csv_parser<CharT,Allocator> parser_;
+ basic_staj_visitor<CharT> cursor_visitor_;
+
+ // Noncopyable and nonmoveable
+ basic_csv_cursor(const basic_csv_cursor&) = delete;
+ basic_csv_cursor& operator=(const basic_csv_cursor&) = delete;
+
+public:
+ using string_view_type = jsoncons::basic_string_view<CharT>;
+
+ // Constructors that throw parse exceptions
+
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>(),
+ std::function<bool(csv_errc,const ser_context&)> err_handler = default_csv_parsing(),
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(std::forward<Sourceable>(source)),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>(),
+ std::function<bool(csv_errc,const ser_context&)> err_handler = default_csv_parsing(),
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all)
+ {
+ jsoncons::basic_string_view<CharT> sv(std::forward<Sourceable>(source));
+ initialize_with_string_view(sv);
+ }
+
+
+ // Constructors that set parse error codes
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ std::error_code& ec)
+ : basic_csv_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ basic_csv_decode_options<CharT>(),
+ default_csv_parsing(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options,
+ std::error_code& ec)
+ : basic_csv_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ default_csv_parsing(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ std::error_code& ec)
+ : basic_csv_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ err_handler,
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ std::error_code& ec,
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(std::forward<Sourceable>(source)),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ std::error_code& ec,
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all)
+ {
+ jsoncons::basic_string_view<CharT> sv(std::forward<Sourceable>(source));
+ initialize_with_string_view(sv, ec);
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source)
+ {
+ source_ = std::forward<Sourceable>(source);
+ parser_.reinitialize();
+ cursor_visitor_.reset();
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source)
+ {
+ source_ = {};
+ parser_.reinitialize();
+ cursor_visitor_.reset();
+ initialize_with_string_view(std::forward<Sourceable>(source));
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source, std::error_code& ec)
+ {
+ source_ = std::forward<Sourceable>(source);
+ parser_.reinitialize();
+ cursor_visitor_.reset();
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source, std::error_code& ec)
+ {
+ source_ = {};
+ parser_.reinitialize();
+ initialize_with_string_view(std::forward<Sourceable>(source), ec);
+ }
+
+ bool done() const override
+ {
+ return parser_.done();
+ }
+
+ const basic_staj_event<CharT>& current() const override
+ {
+ return cursor_visitor_.event();
+ }
+
+ void read_to(basic_json_visitor<CharT>& visitor) override
+ {
+ std::error_code ec;
+ read_to(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_to(basic_json_visitor<CharT>& visitor,
+ std::error_code& ec) override
+ {
+ if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec))
+ {
+ read_next(visitor, ec);
+ }
+ }
+
+ void next() override
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ read_next(ec);
+ }
+
+ static bool accept_all(const basic_staj_event<CharT>&, const ser_context&)
+ {
+ return true;
+ }
+
+ const ser_context& context() const override
+ {
+ return *this;
+ }
+
+ bool eof() const
+ {
+ return parser_.source_exhausted() && source_.eof();
+ }
+
+ std::size_t line() const override
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const override
+ {
+ return parser_.column();
+ }
+
+ friend
+ basic_staj_filter_view<CharT> operator|(basic_csv_cursor& cursor,
+ std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred)
+ {
+ return basic_staj_filter_view<CharT>(cursor, pred);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<CharT>&)")
+ void read(basic_json_visitor<CharT>& visitor)
+ {
+ read_to(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<CharT>&, std::error_code&)")
+ void read(basic_json_visitor<CharT>& visitor,
+ std::error_code& ec)
+ {
+ read_to(visitor, ec);
+ }
+#endif
+private:
+
+ void initialize_with_string_view(string_view_type sv)
+ {
+ auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column()));
+ }
+ std::size_t offset = (r.ptr - sv.data());
+ parser_.update(sv.data()+offset,sv.size()-offset);
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ void initialize_with_string_view(string_view_type sv, std::error_code& ec)
+ {
+ auto r = unicode_traits::detect_encoding_from_bom(sv.data(), sv.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ ec = json_errc::illegal_unicode_character;
+ return;
+ }
+ std::size_t offset = (r.ptr - sv.data());
+ parser_.update(sv.data()+offset,sv.size()-offset);
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ read_next(cursor_visitor_, ec);
+ }
+
+ void read_next(basic_json_visitor<CharT>& visitor, std::error_code& ec)
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ parser_.parse_some(visitor, ec);
+ if (ec) return;
+ }
+ }
+};
+
+using csv_stream_cursor = basic_csv_cursor<char,jsoncons::stream_source<char>>;
+using csv_string_cursor = basic_csv_cursor<char,jsoncons::string_source<char>>;
+using wcsv_stream_cursor = basic_csv_cursor<wchar_t,jsoncons::stream_source<wchar_t>>;
+using wcsv_string_cursor = basic_csv_cursor<wchar_t,jsoncons::string_source<wchar_t>>;
+
+using csv_cursor = basic_csv_cursor<char>;
+using wcsv_cursor = basic_csv_cursor<wchar_t>;
+
+}}
+
+#endif
+
diff --git a/include/jsoncons_ext/csv/csv_encoder.hpp b/include/jsoncons_ext/csv/csv_encoder.hpp
new file mode 100644
index 0000000..49c1a3d
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_encoder.hpp
@@ -0,0 +1,954 @@
+// Copyright 2013 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_CSV_ENCODER_HPP
+#define JSONCONS_CSV_CSV_ENCODER_HPP
+
+#include <array> // std::array
+#include <string>
+#include <vector>
+#include <ostream>
+#include <utility> // std::move
+#include <unordered_map> // std::unordered_map
+#include <memory> // std::allocator
+#include <limits> // std::numeric_limits
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/detail/write_number.hpp>
+#include <jsoncons_ext/csv/csv_options.hpp>
+#include <jsoncons/sink.hpp>
+
+namespace jsoncons { namespace csv {
+
+template<class CharT,class Sink=jsoncons::stream_sink<CharT>,class Allocator=std::allocator<char>>
+class basic_csv_encoder final : public basic_json_visitor<CharT>
+{
+public:
+ using char_type = CharT;
+ using typename basic_json_visitor<CharT>::string_view_type;
+ using sink_type = Sink;
+
+ using allocator_type = Allocator;
+ using char_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<CharT>;
+ using string_type = std::basic_string<CharT, std::char_traits<CharT>, char_allocator_type>;
+ using string_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<string_type>;
+ using string_string_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<std::pair<const string_type,string_type>>;
+
+private:
+ static jsoncons::basic_string_view<CharT> null_constant()
+ {
+ static jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"null");
+ return k;
+ }
+ static jsoncons::basic_string_view<CharT> true_constant()
+ {
+ static jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"true");
+ return k;
+ }
+ static jsoncons::basic_string_view<CharT> false_constant()
+ {
+ static jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"false");
+ return k;
+ }
+
+ enum class stack_item_kind
+ {
+ row_mapping,
+ column_mapping,
+ object,
+ row,
+ column,
+ object_multi_valued_field,
+ row_multi_valued_field,
+ column_multi_valued_field
+ };
+
+ struct stack_item
+ {
+ stack_item_kind item_kind_;
+ std::size_t count_;
+
+ stack_item(stack_item_kind item_kind) noexcept
+ : item_kind_(item_kind), count_(0)
+ {
+ }
+
+ bool is_object() const
+ {
+ return item_kind_ == stack_item_kind::object;
+ }
+
+ stack_item_kind item_kind() const
+ {
+ return item_kind_;
+ }
+ };
+
+ Sink sink_;
+ const basic_csv_encode_options<CharT> options_;
+ allocator_type alloc_;
+
+ std::vector<stack_item> stack_;
+ jsoncons::detail::write_double fp_;
+ std::vector<string_type,string_allocator_type> strings_buffer_;
+
+ std::unordered_map<string_type,string_type, std::hash<string_type>,std::equal_to<string_type>,string_string_allocator_type> buffered_line_;
+ string_type name_;
+ std::size_t column_index_;
+ std::vector<std::size_t> row_counts_;
+
+ // Noncopyable and nonmoveable
+ basic_csv_encoder(const basic_csv_encoder&) = delete;
+ basic_csv_encoder& operator=(const basic_csv_encoder&) = delete;
+public:
+ basic_csv_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_csv_encoder(std::forward<Sink>(sink), basic_csv_encode_options<CharT>(), alloc)
+ {
+ }
+
+ basic_csv_encoder(Sink&& sink,
+ const basic_csv_encode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ alloc_(alloc),
+ stack_(),
+ fp_(options.float_format(), options.precision()),
+ column_index_(0)
+ {
+ jsoncons::csv::detail::parse_column_names(options.column_names(), strings_buffer_);
+ }
+
+ ~basic_csv_encoder() noexcept
+ {
+ JSONCONS_TRY
+ {
+ sink_.flush();
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ strings_buffer_.clear();
+ buffered_line_.clear();
+ name_.clear();
+ column_index_ = 0;
+ row_counts_.clear();
+ }
+
+ void reset(Sink&& sink)
+ {
+ sink_ = std::move(sink);
+ reset();
+ }
+
+private:
+
+ template<class AnyWriter>
+ void escape_string(const CharT* s,
+ std::size_t length,
+ CharT quote_char, CharT quote_escape_char,
+ AnyWriter& sink)
+ {
+ const CharT* begin = s;
+ const CharT* end = s + length;
+ for (const CharT* it = begin; it != end; ++it)
+ {
+ CharT c = *it;
+ if (c == quote_char)
+ {
+ sink.push_back(quote_escape_char);
+ sink.push_back(quote_char);
+ }
+ else
+ {
+ sink.push_back(c);
+ }
+ }
+ }
+
+ void visit_flush() override
+ {
+ sink_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ stack_.emplace_back(stack_item_kind::column_mapping);
+ return true;
+ }
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::row_mapping:
+ stack_.emplace_back(stack_item_kind::object);
+ return true;
+ default: // error
+ ec = csv_errc::source_error;
+ return false;
+ }
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ if (stack_[0].count_ == 0)
+ {
+ for (std::size_t i = 0; i < strings_buffer_.size(); ++i)
+ {
+ if (i > 0)
+ {
+ sink_.push_back(options_.field_delimiter());
+ }
+ sink_.append(strings_buffer_[i].data(),
+ strings_buffer_[i].length());
+ }
+ sink_.append(options_.line_delimiter().data(),
+ options_.line_delimiter().length());
+ }
+ for (std::size_t i = 0; i < strings_buffer_.size(); ++i)
+ {
+ if (i > 0)
+ {
+ sink_.push_back(options_.field_delimiter());
+ }
+ auto it = buffered_line_.find(strings_buffer_[i]);
+ if (it != buffered_line_.end())
+ {
+ sink_.append(it->second.data(),it->second.length());
+ it->second.clear();
+ }
+ }
+ sink_.append(options_.line_delimiter().data(), options_.line_delimiter().length());
+ break;
+ case stack_item_kind::column_mapping:
+ {
+ for (const auto& item : strings_buffer_)
+ {
+ sink_.append(item.data(), item.size());
+ sink_.append(options_.line_delimiter().data(), options_.line_delimiter().length());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ stack_.pop_back();
+ if (!stack_.empty())
+ {
+ end_value();
+ }
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ stack_.emplace_back(stack_item_kind::row_mapping);
+ return true;
+ }
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::row_mapping:
+ stack_.emplace_back(stack_item_kind::row);
+ if (stack_[0].count_ == 0)
+ {
+ for (std::size_t i = 0; i < strings_buffer_.size(); ++i)
+ {
+ if (i > 0)
+ {
+ sink_.push_back(options_.field_delimiter());
+ }
+ sink_.append(strings_buffer_[i].data(),strings_buffer_[i].length());
+ }
+ if (strings_buffer_.size() > 0)
+ {
+ sink_.append(options_.line_delimiter().data(),
+ options_.line_delimiter().length());
+ }
+ }
+ return true;
+ case stack_item_kind::object:
+ stack_.emplace_back(stack_item_kind::object_multi_valued_field);
+ return true;
+ case stack_item_kind::column_mapping:
+ stack_.emplace_back(stack_item_kind::column);
+ row_counts_.push_back(1);
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ return true;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ begin_value(bo);
+ stack_.emplace_back(stack_item_kind::column_multi_valued_field);
+ return true;
+ }
+ case stack_item_kind::row:
+ begin_value(sink_);
+ stack_.emplace_back(stack_item_kind::row_multi_valued_field);
+ return true;
+ default: // error
+ ec = csv_errc::source_error;
+ return false;
+ }
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::row:
+ sink_.append(options_.line_delimiter().data(),
+ options_.line_delimiter().length());
+ break;
+ case stack_item_kind::column:
+ ++column_index_;
+ break;
+ default:
+ break;
+ }
+ stack_.pop_back();
+
+ if (!stack_.empty())
+ {
+ end_value();
+ }
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ {
+ name_ = string_type(name);
+ buffered_line_[string_type(name)] = std::basic_string<CharT>();
+ if (stack_[0].count_ == 0 && options_.column_names().size() == 0)
+ {
+ strings_buffer_.emplace_back(name);
+ }
+ break;
+ }
+ case stack_item_kind::column_mapping:
+ {
+ if (strings_buffer_.empty())
+ {
+ strings_buffer_.emplace_back(name);
+ }
+ else
+ {
+ strings_buffer_[0].push_back(options_.field_delimiter());
+ strings_buffer_[0].append(string_type(name));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_null_value(bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_null_value(sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_null_value(bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_null_value(bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_string_value(sv,bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_string_value(sv,sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_string_value(sv,bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_string_value(sv,bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ byte_string_chars_format encoding_hint;
+ switch (tag)
+ {
+ case semantic_tag::base16:
+ encoding_hint = byte_string_chars_format::base16;
+ break;
+ case semantic_tag::base64:
+ encoding_hint = byte_string_chars_format::base64;
+ break;
+ case semantic_tag::base64url:
+ encoding_hint = byte_string_chars_format::base64url;
+ break;
+ default:
+ encoding_hint = byte_string_chars_format::none;
+ break;
+ }
+ byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(encoding_hint,byte_string_chars_format::none,byte_string_chars_format::base64url);
+
+ std::basic_string<CharT> s;
+ switch (format)
+ {
+ case byte_string_chars_format::base16:
+ {
+ encode_base16(b.begin(),b.end(),s);
+ visit_string(s, semantic_tag::none, context, ec);
+ break;
+ }
+ case byte_string_chars_format::base64:
+ {
+ encode_base64(b.begin(),b.end(),s);
+ visit_string(s, semantic_tag::none, context, ec);
+ break;
+ }
+ case byte_string_chars_format::base64url:
+ {
+ encode_base64url(b.begin(),b.end(),s);
+ visit_string(s, semantic_tag::none, context, ec);
+ break;
+ }
+ default:
+ {
+ JSONCONS_UNREACHABLE();
+ }
+ }
+
+ return true;
+ }
+
+ bool visit_double(double val,
+ semantic_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_double_value(val, context, bo, ec);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_double_value(val, context, sink_, ec);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_double_value(val, context, bo, ec);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_double_value(val, context, bo, ec);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_int64(int64_t val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_int64_value(val,bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_int64_value(val,sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_int64_value(val, bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_int64_value(val, bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_uint64(uint64_t val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_uint64_value(val, bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_uint64_value(val,sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_uint64_value(val, bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_uint64_value(val, bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_bool_value(val,bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_bool_value(val,sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_bool_value(val, bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_bool_value(val, bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ template <class AnyWriter>
+ bool do_string_value(const CharT* s, std::size_t length, AnyWriter& sink)
+ {
+ bool quote = false;
+ if (options_.quote_style() == quote_style_kind::all || options_.quote_style() == quote_style_kind::nonnumeric ||
+ (options_.quote_style() == quote_style_kind::minimal &&
+ (std::char_traits<CharT>::find(s, length, options_.field_delimiter()) != nullptr || std::char_traits<CharT>::find(s, length, options_.quote_char()) != nullptr)))
+ {
+ quote = true;
+ sink.push_back(options_.quote_char());
+ }
+ escape_string(s, length, options_.quote_char(), options_.quote_escape_char(), sink);
+ if (quote)
+ {
+ sink.push_back(options_.quote_char());
+ }
+
+ return true;
+ }
+
+ template <class AnyWriter>
+ void write_string_value(const string_view_type& value, AnyWriter& sink)
+ {
+ begin_value(sink);
+ do_string_value(value.data(),value.length(),sink);
+ end_value();
+ }
+
+ template <class AnyWriter>
+ void write_double_value(double val, const ser_context& context, AnyWriter& sink, std::error_code& ec)
+ {
+ begin_value(sink);
+
+ if (!std::isfinite(val))
+ {
+ if ((std::isnan)(val))
+ {
+ if (options_.enable_nan_to_num())
+ {
+ sink.append(options_.nan_to_num().data(), options_.nan_to_num().length());
+ }
+ else if (options_.enable_nan_to_str())
+ {
+ visit_string(options_.nan_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink.append(null_constant().data(), null_constant().size());
+ }
+ }
+ else if (val == std::numeric_limits<double>::infinity())
+ {
+ if (options_.enable_inf_to_num())
+ {
+ sink.append(options_.inf_to_num().data(), options_.inf_to_num().length());
+ }
+ else if (options_.enable_inf_to_str())
+ {
+ visit_string(options_.inf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink.append(null_constant().data(), null_constant().size());
+ }
+ }
+ else
+ {
+ if (options_.enable_neginf_to_num())
+ {
+ sink.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
+ }
+ else if (options_.enable_neginf_to_str())
+ {
+ visit_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink.append(null_constant().data(), null_constant().size());
+ }
+ }
+ }
+ else
+ {
+ fp_(val, sink);
+ }
+
+ end_value();
+
+ }
+
+ template <class AnyWriter>
+ void write_int64_value(int64_t val, AnyWriter& sink)
+ {
+ begin_value(sink);
+
+ jsoncons::detail::from_integer(val,sink);
+
+ end_value();
+ }
+
+ template <class AnyWriter>
+ void write_uint64_value(uint64_t val, AnyWriter& sink)
+ {
+ begin_value(sink);
+
+ jsoncons::detail::from_integer(val,sink);
+
+ end_value();
+ }
+
+ template <class AnyWriter>
+ void write_bool_value(bool val, AnyWriter& sink)
+ {
+ begin_value(sink);
+
+ if (val)
+ {
+ sink.append(true_constant().data(), true_constant().size());
+ }
+ else
+ {
+ sink.append(false_constant().data(), false_constant().size());
+ }
+
+ end_value();
+ }
+
+ template <class AnyWriter>
+ bool write_null_value(AnyWriter& sink)
+ {
+ begin_value(sink);
+ sink.append(null_constant().data(), null_constant().size());
+ end_value();
+ return true;
+ }
+
+ template <class AnyWriter>
+ void begin_value(AnyWriter& sink)
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::row:
+ if (stack_.back().count_ > 0)
+ {
+ sink.push_back(options_.field_delimiter());
+ }
+ break;
+ case stack_item_kind::column:
+ {
+ if (row_counts_.size() >= 3)
+ {
+ for (std::size_t i = row_counts_.size()-2; i-- > 0;)
+ {
+ if (row_counts_[i] <= row_counts_.back())
+ {
+ sink.push_back(options_.field_delimiter());
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ if (column_index_ > 0)
+ {
+ sink.push_back(options_.field_delimiter());
+ }
+ break;
+ }
+ case stack_item_kind::row_multi_valued_field:
+ case stack_item_kind::column_multi_valued_field:
+ if (stack_.back().count_ > 0 && options_.subfield_delimiter() != char_type())
+ {
+ sink.push_back(options_.subfield_delimiter());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ void end_value()
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch(stack_.back().item_kind_)
+ {
+ case stack_item_kind::row:
+ {
+ ++stack_.back().count_;
+ break;
+ }
+ case stack_item_kind::column:
+ {
+ ++row_counts_.back();
+ break;
+ }
+ default:
+ ++stack_.back().count_;
+ break;
+ }
+ }
+};
+
+using csv_stream_encoder = basic_csv_encoder<char>;
+using csv_string_encoder = basic_csv_encoder<char,jsoncons::string_sink<std::string>>;
+using csv_wstream_encoder = basic_csv_encoder<wchar_t>;
+using wcsv_string_encoder = basic_csv_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+template<class CharT, class Sink = jsoncons::stream_sink<CharT>, class Allocator = std::allocator<CharT>>
+using basic_csv_serializer = basic_csv_encoder<CharT,Sink,Allocator>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use csv_stream_encoder") typedef csv_stream_encoder csv_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_string_encoder") typedef csv_string_encoder csv_string_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_stream_encoder") typedef csv_stream_encoder csv_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_string_encoder") typedef csv_string_encoder csv_string_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_stream_encoder") typedef csv_stream_encoder csv_encoder;
+JSONCONS_DEPRECATED_MSG("Instead, use wcsv_stream_encoder") typedef csv_stream_encoder wcsv_encoder;
+#endif
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/csv/csv_error.hpp b/include/jsoncons_ext/csv/csv_error.hpp
new file mode 100644
index 0000000..30255dd
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_error.hpp
@@ -0,0 +1,85 @@
+/// Copyright 2013 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_CSV_ERROR_HPP
+#define JSONCONS_CSV_CSV_ERROR_HPP
+
+#include <system_error>
+#include <jsoncons/json_exception.hpp>
+
+namespace jsoncons { namespace csv {
+
+ enum class csv_errc : int
+ {
+ success = 0,
+ unexpected_eof = 1,
+ source_error,
+ expected_quote,
+ syntax_error,
+ invalid_parse_state,
+ invalid_escaped_char,
+ unexpected_char_between_fields
+ };
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use csv_errc") typedef csv_errc csv_parser_errc;
+#endif
+
+class csv_error_category_impl
+ : public std::error_category
+{
+public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/csv";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<csv_errc>(ev))
+ {
+ case csv_errc::unexpected_eof:
+ return "Unexpected end of file";
+ case csv_errc::source_error:
+ return "Source error";
+ case csv_errc::expected_quote:
+ return "Expected quote character";
+ case csv_errc::syntax_error:
+ return "CSV syntax error";
+ case csv_errc::invalid_parse_state:
+ return "Invalid CSV parser state";
+ case csv_errc::invalid_escaped_char:
+ return "Invalid character following quote escape character";
+ case csv_errc::unexpected_char_between_fields:
+ return "Unexpected character between fields";
+ default:
+ return "Unknown CSV parser error";
+ }
+ }
+};
+
+inline
+const std::error_category& csv_error_category()
+{
+ static csv_error_category_impl instance;
+ return instance;
+}
+
+inline
+std::error_code make_error_code(csv_errc result)
+{
+ return std::error_code(static_cast<int>(result),csv_error_category());
+}
+
+}}
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::csv::csv_errc> : public true_type
+ {
+ };
+}
+
+#endif
diff --git a/include/jsoncons_ext/csv/csv_options.hpp b/include/jsoncons_ext/csv/csv_options.hpp
new file mode 100644
index 0000000..8bd2e22
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_options.hpp
@@ -0,0 +1,973 @@
+// Copyright 2013 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_CSV_OPTIONS_HPP
+#define JSONCONS_CSV_CSV_OPTIONS_HPP
+
+#include <string>
+#include <vector>
+#include <utility> // std::pair
+#include <unordered_map> // std::unordered_map
+#include <map>
+#include <limits> // std::numeric_limits
+#include <cwchar>
+#include <jsoncons/json_options.hpp>
+
+namespace jsoncons { namespace csv {
+
+enum class csv_column_type : uint8_t
+{
+ string_t,integer_t,float_t,boolean_t,repeat_t
+};
+
+enum class quote_style_kind : uint8_t
+{
+ minimal,all,nonnumeric,none
+};
+
+enum class csv_mapping_kind : uint8_t
+{
+ n_rows = 1,
+ n_objects,
+ m_columns
+};
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+using mapping_kind = csv_mapping_kind;
+JSONCONS_DEPRECATED_MSG("Instead, use quote_style_kind") typedef quote_style_kind quote_styles;
+JSONCONS_DEPRECATED_MSG("Instead, use quote_style_kind") typedef quote_style_kind quote_style_type;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_mapping_kind") typedef csv_mapping_kind mapping_type;
+#endif
+
+enum class column_state {sequence,label};
+
+struct csv_type_info
+{
+ csv_type_info() = default;
+ csv_type_info(const csv_type_info&) = default;
+ csv_type_info(csv_type_info&&) = default;
+
+ csv_type_info(csv_column_type ctype, std::size_t lev, std::size_t repcount = 0) noexcept
+ {
+ col_type = ctype;
+ level = lev;
+ rep_count = repcount;
+ }
+
+ csv_column_type col_type;
+ std::size_t level;
+ std::size_t rep_count;
+};
+
+namespace detail {
+
+template <class CharT,class Container>
+void parse_column_names(const std::basic_string<CharT>& names,
+ Container& cont)
+{
+ column_state state = column_state::sequence;
+ typename Container::value_type buffer(cont.get_allocator());
+
+ auto p = names.begin();
+ while (p != names.end())
+ {
+ switch (state)
+ {
+ case column_state::sequence:
+ {
+ switch (*p)
+ {
+ case ' ': case '\t':case '\r': case '\n':
+ ++p;
+ break;
+ default:
+ buffer.clear();
+ state = column_state::label;
+ break;
+ }
+ break;
+ }
+ case column_state::label:
+ {
+ switch (*p)
+ {
+ case ',':
+ cont.push_back(buffer);
+ buffer.clear();
+ ++p;
+ state = column_state::sequence;
+ break;
+ default:
+ buffer.push_back(*p);
+ ++p;
+ break;
+ }
+ break;
+ }
+ }
+ }
+ if (state == column_state::label)
+ {
+ cont.push_back(buffer);
+ buffer.clear();
+ }
+}
+
+template <class CharT,class Container>
+void parse_column_types(const std::basic_string<CharT>& types,
+ Container& column_types)
+{
+ const std::map<jsoncons::basic_string_view<CharT>,csv_column_type> type_dictionary =
+ {
+
+ {JSONCONS_STRING_VIEW_CONSTANT(CharT,"string"),csv_column_type::string_t},
+ {JSONCONS_STRING_VIEW_CONSTANT(CharT,"integer"),csv_column_type::integer_t},
+ {JSONCONS_STRING_VIEW_CONSTANT(CharT,"float"),csv_column_type::float_t},
+ {JSONCONS_STRING_VIEW_CONSTANT(CharT,"boolean"),csv_column_type::boolean_t}
+ };
+
+ column_state state = column_state::sequence;
+ int depth = 0;
+ std::basic_string<CharT> buffer;
+
+ auto p = types.begin();
+ while (p != types.end())
+ {
+ switch (state)
+ {
+ case column_state::sequence:
+ {
+ switch (*p)
+ {
+ case ' ': case '\t':case '\r': case '\n':
+ ++p;
+ break;
+ case '[':
+ ++depth;
+ ++p;
+ break;
+ case ']':
+ JSONCONS_ASSERT(depth > 0);
+ --depth;
+ ++p;
+ break;
+ case '*':
+ {
+ JSONCONS_ASSERT(column_types.size() != 0);
+ std::size_t offset = 0;
+ std::size_t level = column_types.size() > 0 ? column_types.back().level: 0;
+ if (level > 0)
+ {
+ for (auto it = column_types.rbegin();
+ it != column_types.rend() && level == it->level;
+ ++it)
+ {
+ ++offset;
+ }
+ }
+ else
+ {
+ offset = 1;
+ }
+ column_types.emplace_back(csv_column_type::repeat_t,depth,offset);
+ ++p;
+ break;
+ }
+ default:
+ buffer.clear();
+ state = column_state::label;
+ break;
+ }
+ break;
+ }
+ case column_state::label:
+ {
+ switch (*p)
+ {
+ case '*':
+ {
+ auto it = type_dictionary.find(buffer);
+ if (it != type_dictionary.end())
+ {
+ column_types.emplace_back(it->second,depth);
+ buffer.clear();
+ }
+ else
+ {
+ JSONCONS_ASSERT(false);
+ }
+ state = column_state::sequence;
+ break;
+ }
+ case ',':
+ {
+ auto it = type_dictionary.find(buffer);
+ if (it != type_dictionary.end())
+ {
+ column_types.emplace_back(it->second,depth);
+ buffer.clear();
+ }
+ else
+ {
+ JSONCONS_ASSERT(false);
+ }
+ ++p;
+ state = column_state::sequence;
+ break;
+ }
+ case ']':
+ {
+ JSONCONS_ASSERT(depth > 0);
+ auto it = type_dictionary.find(buffer);
+ if (it != type_dictionary.end())
+ {
+ column_types.emplace_back(it->second,depth);
+ buffer.clear();
+ }
+ else
+ {
+ JSONCONS_ASSERT(false);
+ }
+ --depth;
+ ++p;
+ state = column_state::sequence;
+ break;
+ }
+ default:
+ {
+ buffer.push_back(*p);
+ ++p;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ if (state == column_state::label)
+ {
+ auto it = type_dictionary.find(buffer);
+ if (it != type_dictionary.end())
+ {
+ column_types.emplace_back(it->second,depth);
+ buffer.clear();
+ }
+ else
+ {
+ JSONCONS_ASSERT(false);
+ }
+ }
+}
+
+} // detail
+
+template <class CharT>
+class basic_csv_options;
+
+template <class CharT>
+class basic_csv_options_common
+{
+ friend class basic_csv_options<CharT>;
+public:
+ using char_type = CharT;
+ using string_type = std::basic_string<CharT>;
+private:
+ char_type field_delimiter_;
+ char_type quote_char_;
+ char_type quote_escape_char_;
+ char_type subfield_delimiter_;
+
+ bool enable_nan_to_num_:1;
+ bool enable_inf_to_num_:1;
+ bool enable_neginf_to_num_:1;
+ bool enable_nan_to_str_:1;
+ bool enable_inf_to_str_:1;
+ bool enable_neginf_to_str_:1;
+ bool enable_str_to_nan_:1;
+ bool enable_str_to_inf_:1;
+ bool enable_str_to_neginf_:1;
+
+ string_type nan_to_num_;
+ string_type inf_to_num_;
+ string_type neginf_to_num_;
+ string_type nan_to_str_;
+ string_type inf_to_str_;
+ string_type neginf_to_str_;
+ string_type column_names_;
+
+protected:
+ basic_csv_options_common()
+ : field_delimiter_(','),
+ quote_char_('\"'),
+ quote_escape_char_('\"'),
+ subfield_delimiter_(char_type()),
+ enable_nan_to_num_(false),
+ enable_inf_to_num_(false),
+ enable_neginf_to_num_(false),
+ enable_nan_to_str_(false),
+ enable_inf_to_str_(false),
+ enable_neginf_to_str_(false),
+ enable_str_to_nan_(false),
+ enable_str_to_inf_(false),
+ enable_str_to_neginf_(false)
+ {
+ }
+
+ basic_csv_options_common(const basic_csv_options_common&) = default;
+ basic_csv_options_common& operator=(const basic_csv_options_common&) = default;
+
+ virtual ~basic_csv_options_common() noexcept = default;
+public:
+
+ char_type field_delimiter() const
+ {
+ return field_delimiter_;
+ }
+
+ const char_type subfield_delimiter() const
+ {
+ return subfield_delimiter_;
+ }
+
+ char_type quote_char() const
+ {
+ return quote_char_;
+ }
+
+ char_type quote_escape_char() const
+ {
+ return quote_escape_char_;
+ }
+
+ string_type column_names() const
+ {
+ return column_names_;
+ }
+
+ bool enable_nan_to_num() const
+ {
+ return enable_nan_to_num_;
+ }
+
+ bool enable_inf_to_num() const
+ {
+ return enable_inf_to_num_;
+ }
+
+ bool enable_neginf_to_num() const
+ {
+ return enable_neginf_to_num_ || enable_inf_to_num_;
+ }
+
+ bool enable_nan_to_str() const
+ {
+ return enable_nan_to_str_;
+ }
+
+ bool enable_str_to_nan() const
+ {
+ return enable_str_to_nan_;
+ }
+
+ bool enable_inf_to_str() const
+ {
+ return enable_inf_to_str_;
+ }
+
+ bool enable_str_to_inf() const
+ {
+ return enable_str_to_inf_;
+ }
+
+ bool enable_neginf_to_str() const
+ {
+ return enable_neginf_to_str_ || enable_inf_to_str_;
+ }
+
+ bool enable_str_to_neginf() const
+ {
+ return enable_str_to_neginf_ || enable_str_to_inf_;
+ }
+
+ string_type nan_to_num() const
+ {
+ return nan_to_num_;
+ }
+
+ string_type inf_to_num() const
+ {
+ return inf_to_num_;
+ }
+
+ string_type neginf_to_num() const
+ {
+ if (enable_neginf_to_num_)
+ {
+ return neginf_to_num_;
+ }
+ else if (enable_inf_to_num_)
+ {
+ string_type s;
+ s.push_back('-');
+ s.append(inf_to_num_);
+ return s;
+ }
+ else
+ {
+ return neginf_to_num_;
+ }
+ }
+
+ string_type nan_to_str() const
+ {
+ return nan_to_str_;
+ }
+
+ string_type inf_to_str() const
+ {
+ return inf_to_str_;
+ }
+
+ string_type neginf_to_str() const
+ {
+ if (enable_neginf_to_str_)
+ {
+ return neginf_to_str_;
+ }
+ else if (enable_inf_to_str_)
+ {
+ string_type s;
+ s.push_back('-');
+ s.append(inf_to_str_);
+ return s;
+ }
+ else
+ {
+ return neginf_to_str_; // empty string
+ }
+ }
+};
+
+template <class CharT>
+class basic_csv_decode_options : public virtual basic_csv_options_common<CharT>
+{
+ friend class basic_csv_options<CharT>;
+ using super_type = basic_csv_options_common<CharT>;
+public:
+ using typename super_type::char_type;
+ using typename super_type::string_type;
+
+private:
+ bool assume_header_:1;
+ bool ignore_empty_values_:1;
+ bool ignore_empty_lines_:1;
+ bool trim_leading_:1;
+ bool trim_trailing_:1;
+ bool trim_leading_inside_quotes_:1;
+ bool trim_trailing_inside_quotes_:1;
+ bool unquoted_empty_value_is_null_:1;
+ bool infer_types_:1;
+ bool lossless_number_:1;
+ char_type comment_starter_;
+ csv_mapping_kind mapping_;
+ std::size_t header_lines_;
+ std::size_t max_lines_;
+ string_type column_types_;
+ string_type column_defaults_;
+public:
+ basic_csv_decode_options()
+ : assume_header_(false),
+ ignore_empty_values_(false),
+ ignore_empty_lines_(true),
+ trim_leading_(false),
+ trim_trailing_(false),
+ trim_leading_inside_quotes_(false),
+ trim_trailing_inside_quotes_(false),
+ unquoted_empty_value_is_null_(false),
+ infer_types_(true),
+ lossless_number_(false),
+ comment_starter_('\0'),
+ mapping_(),
+ header_lines_(0),
+ max_lines_((std::numeric_limits<std::size_t>::max)())
+ {}
+
+ basic_csv_decode_options(const basic_csv_decode_options& other) = default;
+
+ basic_csv_decode_options(basic_csv_decode_options&& other)
+ : super_type(std::forward<basic_csv_decode_options>(other)),
+ assume_header_(other.assume_header_),
+ ignore_empty_values_(other.ignore_empty_values_),
+ ignore_empty_lines_(other.ignore_empty_lines_),
+ trim_leading_(other.trim_leading_),
+ trim_trailing_(other.trim_trailing_),
+ trim_leading_inside_quotes_(other.trim_leading_inside_quotes_),
+ trim_trailing_inside_quotes_(other.trim_trailing_inside_quotes_),
+ unquoted_empty_value_is_null_(other.unquoted_empty_value_is_null_),
+ infer_types_(other.infer_types_),
+ lossless_number_(other.lossless_number_),
+ comment_starter_(other.comment_starter_),
+ mapping_(other.mapping_),
+ header_lines_(other.header_lines_),
+ max_lines_(other.max_lines_),
+ column_types_(std::move(other.column_types_)),
+ column_defaults_(std::move(other.column_defaults_))
+ {}
+
+ std::size_t header_lines() const
+ {
+ return (assume_header_ && header_lines_ <= 1) ? 1 : header_lines_;
+ }
+
+ bool assume_header() const
+ {
+ return assume_header_;
+ }
+
+ bool ignore_empty_values() const
+ {
+ return ignore_empty_values_;
+ }
+
+ bool ignore_empty_lines() const
+ {
+ return ignore_empty_lines_;
+ }
+
+ bool trim_leading() const
+ {
+ return trim_leading_;
+ }
+
+ bool trim_trailing() const
+ {
+ return trim_trailing_;
+ }
+
+ bool trim_leading_inside_quotes() const
+ {
+ return trim_leading_inside_quotes_;
+ }
+
+ bool trim_trailing_inside_quotes() const
+ {
+ return trim_trailing_inside_quotes_;
+ }
+
+ bool trim() const
+ {
+ return trim_leading_ && trim_trailing_;
+ }
+
+ bool trim_inside_quotes() const
+ {
+ return trim_leading_inside_quotes_ && trim_trailing_inside_quotes_;
+ }
+
+ bool unquoted_empty_value_is_null() const
+ {
+ return unquoted_empty_value_is_null_;
+ }
+
+ bool infer_types() const
+ {
+ return infer_types_;
+ }
+
+ bool lossless_number() const
+ {
+ return lossless_number_;
+ }
+
+ char_type comment_starter() const
+ {
+ return comment_starter_;
+ }
+
+ csv_mapping_kind mapping_kind() const
+ {
+ return mapping_ != csv_mapping_kind() ? mapping_ : (assume_header() || this->column_names().size() > 0 ? csv_mapping_kind::n_objects : csv_mapping_kind::n_rows);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ csv_mapping_kind mapping() const
+ {
+ return mapping_kind();
+ }
+#endif
+
+ std::size_t max_lines() const
+ {
+ return max_lines_;
+ }
+
+ string_type column_types() const
+ {
+ return column_types_;
+ }
+
+ string_type column_defaults() const
+ {
+ return column_defaults_;
+ }
+};
+
+template <class CharT>
+class basic_csv_encode_options : public virtual basic_csv_options_common<CharT>
+{
+ friend class basic_csv_options<CharT>;
+ using super_type = basic_csv_options_common<CharT>;
+public:
+ using typename super_type::char_type;
+ using typename super_type::string_type;
+private:
+ quote_style_kind quote_style_;
+ float_chars_format float_format_;
+ int8_t precision_;
+ string_type line_delimiter_;
+public:
+ basic_csv_encode_options()
+ : quote_style_(quote_style_kind::minimal),
+ float_format_(float_chars_format::general),
+ precision_(0)
+ {
+ line_delimiter_.push_back('\n');
+ }
+
+ basic_csv_encode_options(const basic_csv_encode_options& other) = default;
+
+ basic_csv_encode_options(basic_csv_encode_options&& other)
+ : super_type(std::forward<basic_csv_encode_options>(other)),
+ quote_style_(other.quote_style_),
+ float_format_(other.float_format_),
+ precision_(other.precision_),
+ line_delimiter_(std::move(other.line_delimiter_))
+ {
+ }
+
+ quote_style_kind quote_style() const
+ {
+ return quote_style_;
+ }
+
+ float_chars_format float_format() const
+ {
+ return float_format_;
+ }
+
+ int8_t precision() const
+ {
+ return precision_;
+ }
+
+ string_type line_delimiter() const
+ {
+ return line_delimiter_;
+ }
+};
+
+template <class CharT>
+class basic_csv_options final : public basic_csv_decode_options<CharT>, public basic_csv_encode_options<CharT>
+{
+ using char_type = CharT;
+ using string_type = std::basic_string<CharT>;
+
+public:
+ using basic_csv_decode_options<CharT>::enable_str_to_nan;
+ using basic_csv_decode_options<CharT>::enable_str_to_inf;
+ using basic_csv_decode_options<CharT>::enable_str_to_neginf;
+ using basic_csv_decode_options<CharT>::nan_to_str;
+ using basic_csv_decode_options<CharT>::inf_to_str;
+ using basic_csv_decode_options<CharT>::neginf_to_str;
+ using basic_csv_decode_options<CharT>::nan_to_num;
+ using basic_csv_decode_options<CharT>::inf_to_num;
+ using basic_csv_decode_options<CharT>::neginf_to_num;
+ using basic_csv_decode_options<CharT>::field_delimiter;
+ using basic_csv_decode_options<CharT>::subfield_delimiter;
+ using basic_csv_decode_options<CharT>::quote_char;
+ using basic_csv_decode_options<CharT>::quote_escape_char;
+ using basic_csv_decode_options<CharT>::column_names;
+ using basic_csv_decode_options<CharT>::header_lines;
+ using basic_csv_decode_options<CharT>::assume_header;
+ using basic_csv_decode_options<CharT>::ignore_empty_values;
+ using basic_csv_decode_options<CharT>::ignore_empty_lines;
+ using basic_csv_decode_options<CharT>::trim_leading;
+ using basic_csv_decode_options<CharT>::trim_trailing;
+ using basic_csv_decode_options<CharT>::trim_leading_inside_quotes;
+ using basic_csv_decode_options<CharT>::trim_trailing_inside_quotes;
+ using basic_csv_decode_options<CharT>::trim;
+ using basic_csv_decode_options<CharT>::trim_inside_quotes;
+ using basic_csv_decode_options<CharT>::unquoted_empty_value_is_null;
+ using basic_csv_decode_options<CharT>::infer_types;
+ using basic_csv_decode_options<CharT>::lossless_number;
+ using basic_csv_decode_options<CharT>::comment_starter;
+ using basic_csv_decode_options<CharT>::mapping;
+ using basic_csv_decode_options<CharT>::max_lines;
+ using basic_csv_decode_options<CharT>::column_types;
+ using basic_csv_decode_options<CharT>::column_defaults;
+ using basic_csv_encode_options<CharT>::float_format;
+ using basic_csv_encode_options<CharT>::precision;
+ using basic_csv_encode_options<CharT>::line_delimiter;
+ using basic_csv_encode_options<CharT>::quote_style;
+
+ static constexpr size_t default_indent = 4;
+
+// Constructors
+
+ basic_csv_options() = default;
+ basic_csv_options(const basic_csv_options&) = default;
+ basic_csv_options(basic_csv_options&&) = default;
+ basic_csv_options& operator=(const basic_csv_options&) = default;
+ basic_csv_options& operator=(basic_csv_options&&) = default;
+
+ basic_csv_options& float_format(float_chars_format value)
+ {
+ this->float_format_ = value;
+ return *this;
+ }
+
+ basic_csv_options& precision(int8_t value)
+ {
+ this->precision_ = value;
+ return *this;
+ }
+
+ basic_csv_options& header_lines(std::size_t value)
+ {
+ this->header_lines_ = value;
+ return *this;
+ }
+
+ basic_csv_options& assume_header(bool value)
+ {
+ this->assume_header_ = value;
+ return *this;
+ }
+
+ basic_csv_options& ignore_empty_values(bool value)
+ {
+ this->ignore_empty_values_ = value;
+ return *this;
+ }
+
+ basic_csv_options& ignore_empty_lines(bool value)
+ {
+ this->ignore_empty_lines_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_leading(bool value)
+ {
+ this->trim_leading_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_trailing(bool value)
+ {
+ this->trim_trailing_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_leading_inside_quotes(bool value)
+ {
+ this->trim_leading_inside_quotes_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_trailing_inside_quotes(bool value)
+ {
+ this->trim_trailing_inside_quotes_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim(bool value)
+ {
+ this->trim_leading_ = value;
+ this->trim_trailing_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_inside_quotes(bool value)
+ {
+ this->trim_leading_inside_quotes_ = value;
+ this->trim_trailing_inside_quotes_ = value;
+ return *this;
+ }
+
+ basic_csv_options& unquoted_empty_value_is_null(bool value)
+ {
+ this->unquoted_empty_value_is_null_ = value;
+ return *this;
+ }
+
+ basic_csv_options& column_names(const string_type& value)
+ {
+ this->column_names_ = value;
+ return *this;
+ }
+
+ basic_csv_options& column_types(const string_type& value)
+ {
+ this->column_types_ = value;
+ return *this;
+ }
+
+ basic_csv_options& column_defaults(const string_type& value)
+ {
+ this->column_defaults_ = value;
+ return *this;
+ }
+
+ basic_csv_options& field_delimiter(char_type value)
+ {
+ this->field_delimiter_ = value;
+ return *this;
+ }
+
+ basic_csv_options& subfield_delimiter(char_type value)
+ {
+ this->subfield_delimiter_ = value;
+ return *this;
+ }
+
+ basic_csv_options& line_delimiter(const string_type& value)
+ {
+ this->line_delimiter_ = value;
+ return *this;
+ }
+
+ basic_csv_options& quote_char(char_type value)
+ {
+ this->quote_char_ = value;
+ return *this;
+ }
+
+ basic_csv_options& infer_types(bool value)
+ {
+ this->infer_types_ = value;
+ return *this;
+ }
+
+ basic_csv_options& lossless_number(bool value)
+ {
+ this->lossless_number_ = value;
+ return *this;
+ }
+
+ basic_csv_options& quote_escape_char(char_type value)
+ {
+ this->quote_escape_char_ = value;
+ return *this;
+ }
+
+ basic_csv_options& comment_starter(char_type value)
+ {
+ this->comment_starter_ = value;
+ return *this;
+ }
+
+ basic_csv_options& quote_style(quote_style_kind value)
+ {
+ this->quote_style_ = value;
+ return *this;
+ }
+
+//#if !defined(JSONCONS_NO_DEPRECATED)
+ basic_csv_options& mapping(csv_mapping_kind value)
+ {
+ this->mapping_ = value;
+ return *this;
+ }
+//#endif
+
+ basic_csv_options& mapping_kind(csv_mapping_kind value)
+ {
+ this->mapping_ = value;
+ return *this;
+ }
+
+ basic_csv_options& max_lines(std::size_t value)
+ {
+ this->max_lines_ = value;
+ return *this;
+ }
+
+ basic_csv_options& nan_to_num(const string_type& value)
+ {
+ this->enable_nan_to_num_ = true;
+ this->nan_to_str_.clear();
+ this->nan_to_num_ = value;
+ return *this;
+ }
+
+ basic_csv_options& inf_to_num(const string_type& value)
+ {
+ this->enable_inf_to_num_ = true;
+ this->inf_to_str_.clear();
+ this->inf_to_num_ = value;
+ return *this;
+ }
+
+ basic_csv_options& neginf_to_num(const string_type& value)
+ {
+ this->enable_neginf_to_num_ = true;
+ this->neginf_to_str_.clear();
+ this->neginf_to_num_ = value;
+ return *this;
+ }
+
+ basic_csv_options& nan_to_str(const string_type& value, bool enable_inverse = true)
+ {
+ this->enable_nan_to_str_ = true;
+ this->enable_str_to_nan_ = enable_inverse;
+ this->nan_to_num_.clear();
+ this->nan_to_str_ = value;
+ return *this;
+ }
+
+ basic_csv_options& inf_to_str(const string_type& value, bool enable_inverse = true)
+ {
+ this->enable_inf_to_str_ = true;
+ this->enable_inf_to_str_ = enable_inverse;
+ this->inf_to_num_.clear();
+ this->inf_to_str_ = value;
+ return *this;
+ }
+
+ basic_csv_options& neginf_to_str(const string_type& value, bool enable_inverse = true)
+ {
+ this->enable_neginf_to_str_ = true;
+ this->enable_neginf_to_str_ = enable_inverse;
+ this->neginf_to_num_.clear();
+ this->neginf_to_str_ = value;
+ return *this;
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ JSONCONS_DEPRECATED_MSG("Instead, use float_format(float_chars_format)")
+ basic_csv_options& floating_point_format(float_chars_format value)
+ {
+ this->float_format_ = value;
+ return *this;
+ }
+#endif
+
+};
+
+using csv_options = basic_csv_options<char>;
+using wcsv_options = basic_csv_options<wchar_t>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use csv_options") typedef csv_options csv_parameters;
+JSONCONS_DEPRECATED_MSG("Instead, use wcsv_options") typedef wcsv_options wcsv_parameters;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_options") typedef csv_options csv_serializing_options;
+JSONCONS_DEPRECATED_MSG("Instead, use wcsv_options") typedef wcsv_options wcsv_serializing_options;
+#endif
+
+
+}}
+#endif
diff --git a/include/jsoncons_ext/csv/csv_parser.hpp b/include/jsoncons_ext/csv/csv_parser.hpp
new file mode 100644
index 0000000..37887e2
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_parser.hpp
@@ -0,0 +1,2097 @@
+// Copyright 2015 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_CSV_PARSER_HPP
+#define JSONCONS_CSV_CSV_PARSER_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <sstream>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <cctype>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_reader.hpp>
+#include <jsoncons/json_filter.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons/detail/parse_number.hpp>
+#include <jsoncons_ext/csv/csv_error.hpp>
+#include <jsoncons_ext/csv/csv_options.hpp>
+
+namespace jsoncons { namespace csv {
+
+enum class csv_mode
+{
+ initial,
+ header,
+ data,
+ subfields
+};
+
+enum class csv_parse_state
+{
+ start,
+ cr,
+ column_labels,
+ expect_comment_or_record,
+ expect_record,
+ end_record,
+ no_more_records,
+ comment,
+ between_values,
+ quoted_string,
+ unquoted_string,
+ before_unquoted_string,
+ escaped_value,
+ minus,
+ zero,
+ integer,
+ fraction,
+ exp1,
+ exp2,
+ exp3,
+ accept,
+ before_unquoted_field,
+ before_unquoted_field_tail,
+ before_unquoted_field_tail1,
+ before_last_unquoted_field,
+ before_last_unquoted_field_tail,
+ before_unquoted_subfield,
+ before_unquoted_subfield_tail,
+ before_quoted_subfield,
+ before_quoted_subfield_tail,
+ before_quoted_field,
+ before_quoted_field_tail,
+ before_last_quoted_field,
+ before_last_quoted_field_tail,
+ done
+};
+
+enum class cached_state
+{
+ begin_object,
+ end_object,
+ begin_array,
+ end_array,
+ name,
+ item,
+ done
+};
+
+struct default_csv_parsing
+{
+ bool operator()(csv_errc, const ser_context&) noexcept
+ {
+ return false;
+ }
+};
+
+namespace detail {
+
+ template <class CharT,class TempAllocator>
+ class parse_event
+ {
+ using temp_allocator_type = TempAllocator;
+ using string_view_type = typename basic_json_visitor<CharT>::string_view_type;
+ using char_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT>;
+ using byte_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<uint8_t>;
+ using string_type = std::basic_string<CharT,std::char_traits<CharT>,char_allocator_type>;
+ using byte_string_type = basic_byte_string<byte_allocator_type>;
+
+ staj_event_type event_type;
+ string_type string_value;
+ byte_string_type byte_string_value;
+ union
+ {
+ bool bool_value;
+ int64_t int64_value;
+ uint64_t uint64_value;
+ double double_value;
+ };
+ semantic_tag tag;
+ public:
+ parse_event(staj_event_type event_type, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(event_type),
+ string_value(alloc),
+ byte_string_value(alloc),
+ tag(tag)
+ {
+ }
+
+ parse_event(const string_view_type& value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::string_value),
+ string_value(value.data(),value.length(),alloc),
+ byte_string_value(alloc),
+ tag(tag)
+ {
+ }
+
+ parse_event(const byte_string_view& value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::byte_string_value),
+ string_value(alloc),
+ byte_string_value(value.data(),value.size(),alloc),
+ tag(tag)
+ {
+ }
+
+ parse_event(bool value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::bool_value),
+ string_value(alloc),
+ byte_string_value(alloc),
+ bool_value(value),
+ tag(tag)
+ {
+ }
+
+ parse_event(int64_t value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::int64_value),
+ string_value(alloc),
+ byte_string_value(alloc),
+ int64_value(value),
+ tag(tag)
+ {
+ }
+
+ parse_event(uint64_t value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::uint64_value),
+ string_value(alloc),
+ byte_string_value(alloc),
+ uint64_value(value),
+ tag(tag)
+ {
+ }
+
+ parse_event(double value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::double_value),
+ string_value(alloc),
+ byte_string_value(alloc),
+ double_value(value),
+ tag(tag)
+ {
+ }
+
+ parse_event(const parse_event&) = default;
+ parse_event(parse_event&&) = default;
+ parse_event& operator=(const parse_event&) = default;
+ parse_event& operator=(parse_event&&) = default;
+
+ bool replay(basic_json_visitor<CharT>& visitor) const
+ {
+ switch (event_type)
+ {
+ case staj_event_type::begin_array:
+ return visitor.begin_array(tag, ser_context());
+ case staj_event_type::end_array:
+ return visitor.end_array(ser_context());
+ case staj_event_type::string_value:
+ return visitor.string_value(string_value, tag, ser_context());
+ case staj_event_type::byte_string_value:
+ case staj_event_type::null_value:
+ return visitor.null_value(tag, ser_context());
+ case staj_event_type::bool_value:
+ return visitor.bool_value(bool_value, tag, ser_context());
+ case staj_event_type::int64_value:
+ return visitor.int64_value(int64_value, tag, ser_context());
+ case staj_event_type::uint64_value:
+ return visitor.uint64_value(uint64_value, tag, ser_context());
+ case staj_event_type::double_value:
+ return visitor.double_value(double_value, tag, ser_context());
+ default:
+ return false;
+ }
+ }
+ };
+
+ template <class CharT, class TempAllocator>
+ class m_columns_filter : public basic_json_visitor<CharT>
+ {
+ public:
+ using string_view_type = typename basic_json_visitor<CharT>::string_view_type;
+ using char_type = CharT;
+ using temp_allocator_type = TempAllocator;
+
+ using char_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT>;
+ using string_type = std::basic_string<CharT,std::char_traits<CharT>,char_allocator_type>;
+
+ using string_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<string_type>;
+ using parse_event_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<parse_event<CharT,TempAllocator>>;
+ using parse_event_vector_type = std::vector<parse_event<CharT,TempAllocator>, parse_event_allocator_type>;
+ using parse_event_vector_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<parse_event_vector_type>;
+ private:
+ TempAllocator alloc_;
+ std::size_t name_index_;
+ int level_;
+ cached_state state_;
+ std::size_t column_index_;
+ std::size_t row_index_;
+
+ std::vector<string_type, string_allocator_type> column_names_;
+ std::vector<parse_event_vector_type,parse_event_vector_allocator_type> cached_events_;
+ public:
+
+ m_columns_filter(const TempAllocator& alloc)
+ : alloc_(alloc),
+ name_index_(0),
+ level_(0),
+ state_(cached_state::begin_object),
+ column_index_(0),
+ row_index_(0),
+ column_names_(alloc),
+ cached_events_(alloc)
+ {
+ }
+
+ void reset()
+ {
+ name_index_ = 0;
+ level_ = 0;
+ state_ = cached_state::begin_object;
+ column_index_ = 0;
+ row_index_ = 0;
+ column_names_.clear();
+ cached_events_.clear();
+ }
+
+ bool done() const
+ {
+ return state_ == cached_state::done;
+ }
+
+ void initialize(const std::vector<string_type, string_allocator_type>& column_names)
+ {
+ for (const auto& name : column_names)
+ {
+ column_names_.push_back(name);
+ cached_events_.emplace_back(alloc_);
+ }
+ name_index_ = 0;
+ level_ = 0;
+ column_index_ = 0;
+ row_index_ = 0;
+ state_ = cached_state::begin_object;
+ }
+
+ void skip_column()
+ {
+ ++name_index_;
+ }
+
+ bool replay_parse_events(basic_json_visitor<CharT>& visitor)
+ {
+ bool more = true;
+ while (more)
+ {
+ switch (state_)
+ {
+ case cached_state::begin_object:
+ more = visitor.begin_object(semantic_tag::none, ser_context());
+ column_index_ = 0;
+ state_ = cached_state::name;
+ break;
+ case cached_state::end_object:
+ more = visitor.end_object(ser_context());
+ state_ = cached_state::done;
+ break;
+ case cached_state::name:
+ if (column_index_ < column_names_.size())
+ {
+ more = visitor.key(column_names_[column_index_], ser_context());
+ state_ = cached_state::begin_array;
+ }
+ else
+ {
+ state_ = cached_state::end_object;
+ }
+ break;
+ case cached_state::begin_array:
+ more = visitor.begin_array(semantic_tag::none, ser_context());
+ row_index_ = 0;
+ state_ = cached_state::item;
+ break;
+ case cached_state::end_array:
+ more = visitor.end_array(ser_context());
+ ++column_index_;
+ state_ = cached_state::name;
+ break;
+ case cached_state::item:
+ if (row_index_ < cached_events_[column_index_].size())
+ {
+ more = cached_events_[column_index_][row_index_].replay(visitor);
+ ++row_index_;
+ }
+ else
+ {
+ state_ = cached_state::end_array;
+ }
+ break;
+ default:
+ more = false;
+ break;
+ }
+ }
+ return more;
+ }
+
+ void visit_flush() override
+ {
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ ec = csv_errc::invalid_parse_state;
+ return false;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code& ec) override
+ {
+ ec = csv_errc::invalid_parse_state;
+ return false;
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(staj_event_type::begin_array, tag, alloc_);
+
+ ++level_;
+ }
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ if (level_ > 0)
+ {
+ cached_events_[name_index_].emplace_back(staj_event_type::end_array, semantic_tag::none, alloc_);
+ ++name_index_;
+ --level_;
+ }
+ else
+ {
+ name_index_ = 0;
+ }
+ return true;
+ }
+
+ bool visit_key(const string_view_type&, const ser_context&, std::error_code& ec) override
+ {
+ ec = csv_errc::invalid_parse_state;
+ return false;
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(staj_event_type::null_value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_string(const string_view_type& value, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_double(double value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+ };
+
+} // namespace detail
+
+template<class CharT,class TempAllocator=std::allocator<char>>
+class basic_csv_parser : public ser_context
+{
+public:
+ using string_view_type = jsoncons::basic_string_view<CharT>;
+ using char_type = CharT;
+private:
+ struct string_maps_to_double
+ {
+ string_view_type s;
+
+ bool operator()(const std::pair<string_view_type,double>& val) const
+ {
+ return val.first == s;
+ }
+ };
+
+ using temp_allocator_type = TempAllocator;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT> char_allocator_type;
+ using string_type = std::basic_string<CharT,std::char_traits<CharT>,char_allocator_type>;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<string_type> string_allocator_type;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<csv_mode> csv_mode_allocator_type;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<csv_type_info> csv_type_info_allocator_type;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<std::vector<string_type,string_allocator_type>> string_vector_allocator_type;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<csv_parse_state> csv_parse_state_allocator_type;
+
+ static constexpr int default_depth = 3;
+
+ temp_allocator_type alloc_;
+ csv_parse_state state_;
+ basic_json_visitor<CharT>* visitor_;
+ std::function<bool(csv_errc,const ser_context&)> err_handler_;
+ std::size_t column_;
+ std::size_t line_;
+ int depth_;
+ const basic_csv_decode_options<CharT> options_;
+ std::size_t column_index_;
+ std::size_t level_;
+ std::size_t offset_;
+ jsoncons::detail::chars_to to_double_;
+ const CharT* begin_input_;
+ const CharT* input_end_;
+ const CharT* input_ptr_;
+ bool more_;
+ std::size_t header_line_;
+
+ detail::m_columns_filter<CharT,TempAllocator> m_columns_filter_;
+ std::vector<csv_mode,csv_mode_allocator_type> stack_;
+ std::vector<string_type,string_allocator_type> column_names_;
+ std::vector<csv_type_info,csv_type_info_allocator_type> column_types_;
+ std::vector<string_type,string_allocator_type> column_defaults_;
+ std::vector<csv_parse_state,csv_parse_state_allocator_type> state_stack_;
+ string_type buffer_;
+ std::vector<std::pair<string_view_type,double>> string_double_map_;
+
+public:
+ basic_csv_parser(const TempAllocator& alloc = TempAllocator())
+ : basic_csv_parser(basic_csv_decode_options<CharT>(),
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ basic_csv_parser(const basic_csv_decode_options<CharT>& options,
+ const TempAllocator& alloc = TempAllocator())
+ : basic_csv_parser(options,
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ basic_csv_parser(std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const TempAllocator& alloc = TempAllocator())
+ : basic_csv_parser(basic_csv_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ basic_csv_parser(const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const TempAllocator& alloc = TempAllocator())
+ : alloc_(alloc),
+ state_(csv_parse_state::start),
+ visitor_(nullptr),
+ err_handler_(err_handler),
+ column_(1),
+ line_(1),
+ depth_(default_depth),
+ options_(options),
+ column_index_(0),
+ level_(0),
+ offset_(0),
+ begin_input_(nullptr),
+ input_end_(nullptr),
+ input_ptr_(nullptr),
+ more_(true),
+ header_line_(1),
+ m_columns_filter_(alloc),
+ stack_(alloc),
+ column_names_(alloc),
+ column_types_(alloc),
+ column_defaults_(alloc),
+ state_stack_(alloc),
+ buffer_(alloc)
+ {
+ if (options_.enable_str_to_nan())
+ {
+ string_double_map_.emplace_back(options_.nan_to_str(),std::nan(""));
+ }
+ if (options_.enable_str_to_inf())
+ {
+ string_double_map_.emplace_back(options_.inf_to_str(),std::numeric_limits<double>::infinity());
+ }
+ if (options_.enable_str_to_neginf())
+ {
+ string_double_map_.emplace_back(options_.neginf_to_str(),-std::numeric_limits<double>::infinity());
+ }
+
+ initialize();
+ }
+
+ ~basic_csv_parser() noexcept
+ {
+ }
+
+ bool done() const
+ {
+ return state_ == csv_parse_state::done;
+ }
+
+ bool accept() const
+ {
+ return state_ == csv_parse_state::accept || state_ == csv_parse_state::done;
+ }
+
+ bool stopped() const
+ {
+ return !more_;
+ }
+
+ bool source_exhausted() const
+ {
+ return input_ptr_ == input_end_;
+ }
+
+ const std::vector<string_type,string_allocator_type>& column_labels() const
+ {
+ return column_names_;
+ }
+
+ void reinitialize()
+ {
+ state_ = csv_parse_state::start;
+ visitor_ = nullptr;
+ column_ = 1;
+ line_ = 1;
+ depth_ = default_depth;
+ column_index_ = 0;
+ level_ = 0;
+ offset_ = 0;
+ begin_input_ = nullptr;
+ input_end_ = nullptr;
+ input_ptr_ = nullptr;
+ more_ = true;
+ header_line_ = 1;
+ m_columns_filter_.reset();
+ stack_.clear();
+ column_names_.clear();
+ column_types_.clear();
+ column_defaults_.clear();
+ state_stack_.clear();
+ buffer_.clear();
+
+ initialize();
+ }
+
+ void restart()
+ {
+ more_ = true;
+ }
+
+ void parse_some(basic_json_visitor<CharT>& visitor)
+ {
+ std::error_code ec;
+ parse_some(visitor,ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line_,column_));
+ }
+ }
+
+ void parse_some(basic_json_visitor<CharT>& visitor, std::error_code& ec)
+ {
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::m_columns:
+ visitor_ = &m_columns_filter_;
+ break;
+ default:
+ visitor_ = std::addressof(visitor);
+ break;
+ }
+
+ const CharT* local_input_end = input_end_;
+
+ if (input_ptr_ == local_input_end && more_)
+ {
+ switch (state_)
+ {
+ case csv_parse_state::start:
+ ec = csv_errc::source_error;
+ more_ = false;
+ return;
+ case csv_parse_state::before_unquoted_field:
+ case csv_parse_state::before_last_unquoted_field:
+ end_unquoted_string_value(ec);
+ state_ = csv_parse_state::before_last_unquoted_field_tail;
+ break;
+ case csv_parse_state::before_last_unquoted_field_tail:
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ ++column_index_;
+ state_ = csv_parse_state::end_record;
+ break;
+ case csv_parse_state::before_unquoted_string:
+ buffer_.clear();
+ JSONCONS_FALLTHROUGH;
+ case csv_parse_state::unquoted_string:
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ if (options_.ignore_empty_values() && buffer_.empty())
+ {
+ state_ = csv_parse_state::end_record;
+ }
+ else
+ {
+ before_value(ec);
+ state_ = csv_parse_state::before_unquoted_field;
+ }
+ break;
+ case csv_parse_state::before_last_quoted_field:
+ end_quoted_string_value(ec);
+ ++column_index_;
+ state_ = csv_parse_state::end_record;
+ break;
+ case csv_parse_state::escaped_value:
+ if (options_.quote_escape_char() == options_.quote_char())
+ {
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ before_value(ec);
+ ++column_;
+ state_ = csv_parse_state::before_last_quoted_field;
+ }
+ else
+ {
+ state_ = csv_parse_state::end_record;
+ }
+ }
+ else
+ {
+ ec = csv_errc::invalid_escaped_char;
+ more_ = false;
+ return;
+ }
+ break;
+ case csv_parse_state::end_record:
+ if (column_index_ > 0)
+ {
+ after_record(ec);
+ }
+ state_ = csv_parse_state::no_more_records;
+ break;
+ case csv_parse_state::no_more_records:
+ switch (stack_.back())
+ {
+ case csv_mode::header:
+ stack_.pop_back();
+ break;
+ case csv_mode::data:
+ stack_.pop_back();
+ break;
+ default:
+ break;
+ }
+ more_ = visitor_->end_array(*this, ec);
+ if (options_.mapping_kind() == csv_mapping_kind::m_columns)
+ {
+ if (!m_columns_filter_.done())
+ {
+ more_ = m_columns_filter_.replay_parse_events(visitor);
+ }
+ else
+ {
+ state_ = csv_parse_state::accept;
+ }
+ }
+ else
+ {
+ state_ = csv_parse_state::accept;
+ }
+ break;
+ case csv_parse_state::accept:
+ if (!(stack_.size() == 1 && stack_.back() == csv_mode::initial))
+ {
+ err_handler_(csv_errc::unexpected_eof, *this);
+ ec = csv_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ stack_.pop_back();
+ visitor_->flush();
+ state_ = csv_parse_state::done;
+ more_ = false;
+ return;
+ default:
+ state_ = csv_parse_state::end_record;
+ break;
+ }
+ }
+
+ for (; (input_ptr_ < local_input_end) && more_;)
+ {
+ CharT curr_char = *input_ptr_;
+
+ switch (state_)
+ {
+ case csv_parse_state::cr:
+ ++line_;
+ column_ = 1;
+ switch (*input_ptr_)
+ {
+ case '\n':
+ ++input_ptr_;
+ state_ = pop_state();
+ break;
+ default:
+ state_ = pop_state();
+ break;
+ }
+ break;
+ case csv_parse_state::start:
+ if (options_.mapping_kind() != csv_mapping_kind::m_columns)
+ {
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ }
+ if (options_.assume_header() && options_.mapping_kind() == csv_mapping_kind::n_rows && options_.column_names().size() > 0)
+ {
+ column_index_ = 0;
+ state_ = csv_parse_state::column_labels;
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ state_ = csv_parse_state::expect_comment_or_record;
+ }
+ else
+ {
+ state_ = csv_parse_state::expect_comment_or_record;
+ }
+ break;
+ case csv_parse_state::column_labels:
+ if (column_index_ < column_names_.size())
+ {
+ more_ = visitor_->string_value(column_names_[column_index_], semantic_tag::none, *this, ec);
+ ++column_index_;
+ }
+ else
+ {
+ more_ = visitor_->end_array(*this, ec);
+ state_ = csv_parse_state::expect_comment_or_record;
+ //stack_.back() = csv_mode::data;
+ column_index_ = 0;
+ }
+ break;
+ case csv_parse_state::comment:
+ switch (curr_char)
+ {
+ case '\n':
+ {
+ ++line_;
+ if (stack_.back() == csv_mode::header)
+ {
+ ++header_line_;
+ }
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ break;
+ }
+ case '\r':
+ ++line_;
+ if (stack_.back() == csv_mode::header)
+ {
+ ++header_line_;
+ }
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ push_state(state_);
+ state_ = csv_parse_state::cr;
+ break;
+ default:
+ ++column_;
+ break;
+ }
+ ++input_ptr_;
+ break;
+
+ case csv_parse_state::expect_comment_or_record:
+ buffer_.clear();
+ if (curr_char == options_.comment_starter())
+ {
+ state_ = csv_parse_state::comment;
+ ++column_;
+ ++input_ptr_;
+ }
+ else
+ {
+ state_ = csv_parse_state::expect_record;
+ }
+ break;
+ case csv_parse_state::quoted_string:
+ {
+ if (curr_char == options_.quote_escape_char())
+ {
+ state_ = csv_parse_state::escaped_value;
+ }
+ else if (curr_char == options_.quote_char())
+ {
+ state_ = csv_parse_state::between_values;
+ }
+ else
+ {
+ buffer_.push_back(static_cast<CharT>(curr_char));
+ }
+ }
+ ++column_;
+ ++input_ptr_;
+ break;
+ case csv_parse_state::escaped_value:
+ {
+ if (curr_char == options_.quote_char())
+ {
+ buffer_.push_back(static_cast<CharT>(curr_char));
+ state_ = csv_parse_state::quoted_string;
+ ++column_;
+ ++input_ptr_;
+ }
+ else if (options_.quote_escape_char() == options_.quote_char())
+ {
+ state_ = csv_parse_state::between_values;
+ }
+ else
+ {
+ ec = csv_errc::invalid_escaped_char;
+ more_ = false;
+ return;
+ }
+ }
+ break;
+ case csv_parse_state::between_values:
+ switch (curr_char)
+ {
+ case '\r':
+ case '\n':
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ before_value(ec);
+ state_ = csv_parse_state::before_last_quoted_field;
+ }
+ else
+ {
+ state_ = csv_parse_state::end_record;
+ }
+ break;
+ }
+ default:
+ if (curr_char == options_.field_delimiter())
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ before_value(ec);
+ state_ = csv_parse_state::before_quoted_field;
+ }
+ else if (options_.subfield_delimiter() != char_type() && curr_char == options_.subfield_delimiter())
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ before_value(ec);
+ state_ = csv_parse_state::before_quoted_subfield;
+ }
+ else if (curr_char == ' ' || curr_char == '\t')
+ {
+ ++column_;
+ ++input_ptr_;
+ }
+ else
+ {
+ ec = csv_errc::unexpected_char_between_fields;
+ more_ = false;
+ return;
+ }
+ break;
+ }
+ break;
+ case csv_parse_state::before_unquoted_string:
+ {
+ buffer_.clear();
+ state_ = csv_parse_state::unquoted_string;
+ break;
+ }
+ case csv_parse_state::before_unquoted_field:
+ end_unquoted_string_value(ec);
+ state_ = csv_parse_state::before_unquoted_field_tail;
+ break;
+ case csv_parse_state::before_unquoted_field_tail:
+ {
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ ++column_index_;
+ state_ = csv_parse_state::before_unquoted_string;
+ ++column_;
+ ++input_ptr_;
+ break;
+ }
+ case csv_parse_state::before_unquoted_field_tail1:
+ {
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ state_ = csv_parse_state::end_record;
+ ++column_;
+ ++input_ptr_;
+ break;
+ }
+
+ case csv_parse_state::before_last_unquoted_field:
+ end_unquoted_string_value(ec);
+ state_ = csv_parse_state::before_last_unquoted_field_tail;
+ break;
+
+ case csv_parse_state::before_last_unquoted_field_tail:
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ ++column_index_;
+ state_ = csv_parse_state::end_record;
+ break;
+
+ case csv_parse_state::before_unquoted_subfield:
+ if (stack_.back() == csv_mode::data)
+ {
+ stack_.push_back(csv_mode::subfields);
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ }
+ state_ = csv_parse_state::before_unquoted_subfield_tail;
+ break;
+ case csv_parse_state::before_unquoted_subfield_tail:
+ end_unquoted_string_value(ec);
+ state_ = csv_parse_state::before_unquoted_string;
+ ++column_;
+ ++input_ptr_;
+ break;
+ case csv_parse_state::before_quoted_field:
+ end_quoted_string_value(ec);
+ state_ = csv_parse_state::before_unquoted_field_tail; // return to unquoted
+ break;
+ case csv_parse_state::before_quoted_subfield:
+ if (stack_.back() == csv_mode::data)
+ {
+ stack_.push_back(csv_mode::subfields);
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ }
+ state_ = csv_parse_state::before_quoted_subfield_tail;
+ break;
+ case csv_parse_state::before_quoted_subfield_tail:
+ end_quoted_string_value(ec);
+ state_ = csv_parse_state::before_unquoted_string;
+ ++column_;
+ ++input_ptr_;
+ break;
+ case csv_parse_state::before_last_quoted_field:
+ end_quoted_string_value(ec);
+ state_ = csv_parse_state::before_last_quoted_field_tail;
+ break;
+ case csv_parse_state::before_last_quoted_field_tail:
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ ++column_index_;
+ state_ = csv_parse_state::end_record;
+ break;
+ case csv_parse_state::unquoted_string:
+ {
+ switch (curr_char)
+ {
+ case '\n':
+ case '\r':
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ before_value(ec);
+ state_ = csv_parse_state::before_last_unquoted_field;
+ }
+ else
+ {
+ state_ = csv_parse_state::end_record;
+ }
+ break;
+ }
+ default:
+ if (curr_char == options_.field_delimiter())
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ before_value(ec);
+ state_ = csv_parse_state::before_unquoted_field;
+ }
+ else if (options_.subfield_delimiter() != char_type() && curr_char == options_.subfield_delimiter())
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ before_value(ec);
+ state_ = csv_parse_state::before_unquoted_subfield;
+ }
+ else if (curr_char == options_.quote_char())
+ {
+ buffer_.clear();
+ state_ = csv_parse_state::quoted_string;
+ ++column_;
+ ++input_ptr_;
+ }
+ else
+ {
+ buffer_.push_back(static_cast<CharT>(curr_char));
+ ++column_;
+ ++input_ptr_;
+ }
+ break;
+ }
+ break;
+ }
+ case csv_parse_state::expect_record:
+ {
+ switch (curr_char)
+ {
+ case '\n':
+ {
+ if (!options_.ignore_empty_lines())
+ {
+ before_record(ec);
+ state_ = csv_parse_state::end_record;
+ }
+ else
+ {
+ ++line_;
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ ++input_ptr_;
+ }
+ break;
+ }
+ case '\r':
+ if (!options_.ignore_empty_lines())
+ {
+ before_record(ec);
+ state_ = csv_parse_state::end_record;
+ }
+ else
+ {
+ ++line_;
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ ++input_ptr_;
+ push_state(state_);
+ state_ = csv_parse_state::cr;
+ }
+ break;
+ case ' ':
+ case '\t':
+ if (!options_.trim_leading())
+ {
+ buffer_.push_back(static_cast<CharT>(curr_char));
+ before_record(ec);
+ state_ = csv_parse_state::unquoted_string;
+ }
+ ++column_;
+ ++input_ptr_;
+ break;
+ default:
+ before_record(ec);
+ if (curr_char == options_.quote_char())
+ {
+ buffer_.clear();
+ state_ = csv_parse_state::quoted_string;
+ ++column_;
+ ++input_ptr_;
+ }
+ else
+ {
+ state_ = csv_parse_state::unquoted_string;
+ }
+ break;
+ }
+ break;
+ }
+ case csv_parse_state::end_record:
+ {
+ switch (curr_char)
+ {
+ case '\n':
+ {
+ ++line_;
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ after_record(ec);
+ ++input_ptr_;
+ break;
+ }
+ case '\r':
+ ++line_;
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ after_record(ec);
+ push_state(state_);
+ state_ = csv_parse_state::cr;
+ ++input_ptr_;
+ break;
+ case ' ':
+ case '\t':
+ ++column_;
+ ++input_ptr_;
+ break;
+ default:
+ err_handler_(csv_errc::syntax_error, *this);
+ ec = csv_errc::syntax_error;
+ more_ = false;
+ return;
+ }
+ break;
+ }
+ default:
+ err_handler_(csv_errc::invalid_parse_state, *this);
+ ec = csv_errc::invalid_parse_state;
+ more_ = false;
+ return;
+ }
+ if (line_ > options_.max_lines())
+ {
+ state_ = csv_parse_state::done;
+ more_ = false;
+ }
+ }
+ }
+
+ void finish_parse()
+ {
+ std::error_code ec;
+ finish_parse(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line_,column_));
+ }
+ }
+
+ void finish_parse(std::error_code& ec)
+ {
+ while (more_)
+ {
+ parse_some(ec);
+ }
+ }
+
+ csv_parse_state state() const
+ {
+ return state_;
+ }
+
+ void update(const string_view_type sv)
+ {
+ update(sv.data(),sv.length());
+ }
+
+ void update(const CharT* data, std::size_t length)
+ {
+ begin_input_ = data;
+ input_end_ = data + length;
+ input_ptr_ = begin_input_;
+ }
+
+ std::size_t line() const override
+ {
+ return line_;
+ }
+
+ std::size_t column() const override
+ {
+ return column_;
+ }
+
+private:
+ void initialize()
+ {
+ jsoncons::csv::detail::parse_column_names(options_.column_names(), column_names_);
+ jsoncons::csv::detail::parse_column_types(options_.column_types(), column_types_);
+ jsoncons::csv::detail::parse_column_names(options_.column_defaults(), column_defaults_);
+
+ stack_.reserve(default_depth);
+ stack_.push_back(csv_mode::initial);
+ stack_.push_back((options_.header_lines() > 0) ? csv_mode::header
+ : csv_mode::data);
+ }
+
+ // name
+ void before_value(std::error_code& ec)
+ {
+ switch (stack_.back())
+ {
+ case csv_mode::header:
+ if (options_.trim_leading_inside_quotes() || options_.trim_trailing_inside_quotes())
+ {
+ trim_string_buffer(options_.trim_leading_inside_quotes(),options_.trim_trailing_inside_quotes());
+ }
+ if (line_ == header_line_)
+ {
+ column_names_.push_back(buffer_);
+ if (options_.assume_header() && options_.mapping_kind() == csv_mapping_kind::n_rows)
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec);
+ }
+ }
+ break;
+ case csv_mode::data:
+ if (options_.mapping_kind() == csv_mapping_kind::n_objects)
+ {
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ if (column_index_ < column_names_.size() + offset_)
+ {
+ more_ = visitor_->key(column_names_[column_index_ - offset_], *this, ec);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // begin_array or begin_record
+ void before_record(std::error_code& ec)
+ {
+ offset_ = 0;
+
+ switch (stack_.back())
+ {
+ case csv_mode::header:
+ if (options_.assume_header() && line_ == header_line_)
+ {
+ if (options_.mapping_kind() == csv_mapping_kind::n_rows)
+ {
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ }
+ }
+ break;
+ case csv_mode::data:
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ break;
+ case csv_mapping_kind::n_objects:
+ more_ = visitor_->begin_object(semantic_tag::none, *this, ec);
+ break;
+ case csv_mapping_kind::m_columns:
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // end_array, begin_array, string_value (headers)
+ void after_record(std::error_code& ec)
+ {
+ if (column_types_.size() > 0)
+ {
+ if (level_ > 0)
+ {
+ more_ = visitor_->end_array(*this, ec);
+ level_ = 0;
+ }
+ }
+ switch (stack_.back())
+ {
+ case csv_mode::header:
+ if (line_ >= options_.header_lines())
+ {
+ stack_.back() = csv_mode::data;
+ }
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ if (options_.assume_header())
+ {
+ more_ = visitor_->end_array(*this, ec);
+ }
+ break;
+ case csv_mapping_kind::m_columns:
+ m_columns_filter_.initialize(column_names_);
+ break;
+ default:
+ break;
+ }
+ break;
+ case csv_mode::data:
+ case csv_mode::subfields:
+ {
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ more_ = visitor_->end_array(*this, ec);
+ break;
+ case csv_mapping_kind::n_objects:
+ more_ = visitor_->end_object(*this, ec);
+ break;
+ case csv_mapping_kind::m_columns:
+ more_ = visitor_->end_array(*this, ec);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ column_index_ = 0;
+ }
+
+ void trim_string_buffer(bool trim_leading, bool trim_trailing)
+ {
+ std::size_t start = 0;
+ std::size_t length = buffer_.length();
+ if (trim_leading)
+ {
+ bool done = false;
+ while (!done && start < buffer_.length())
+ {
+ if ((buffer_[start] < 256) && std::isspace(buffer_[start]))
+ {
+ ++start;
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ }
+ if (trim_trailing)
+ {
+ bool done = false;
+ while (!done && length > 0)
+ {
+ if ((buffer_[length-1] < 256) && std::isspace(buffer_[length-1]))
+ {
+ --length;
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ }
+ if (start != 0 || length != buffer_.size())
+ {
+ buffer_ = buffer_.substr(start,length-start);
+ }
+ }
+
+ /*
+ end_array, begin_array, xxx_value (end_value)
+ */
+ void end_unquoted_string_value(std::error_code& ec)
+ {
+ switch (stack_.back())
+ {
+ case csv_mode::data:
+ case csv_mode::subfields:
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(options_.infer_types(), ec);
+ }
+ break;
+ case csv_mapping_kind::n_objects:
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ if (column_index_ < column_names_.size() + offset_)
+ {
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(options_.infer_types(), ec);
+ }
+ }
+ else if (level_ > 0)
+ {
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(options_.infer_types(), ec);
+ }
+ }
+ }
+ break;
+ case csv_mapping_kind::m_columns:
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ end_value(options_.infer_types(), ec);
+ }
+ else
+ {
+ m_columns_filter_.skip_column();
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ void end_quoted_string_value(std::error_code& ec)
+ {
+ switch (stack_.back())
+ {
+ case csv_mode::data:
+ case csv_mode::subfields:
+ if (options_.trim_leading_inside_quotes() || options_.trim_trailing_inside_quotes())
+ {
+ trim_string_buffer(options_.trim_leading_inside_quotes(),options_.trim_trailing_inside_quotes());
+ }
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ end_value(false, ec);
+ break;
+ case csv_mapping_kind::n_objects:
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ if (column_index_ < column_names_.size() + offset_)
+ {
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(false, ec);
+ }
+ }
+ else if (level_ > 0)
+ {
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(false, ec);
+ }
+ }
+ }
+ break;
+ case csv_mapping_kind::m_columns:
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ end_value(false, ec);
+ }
+ else
+ {
+ m_columns_filter_.skip_column();
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ void end_value(bool infer_types, std::error_code& ec)
+ {
+ auto it = std::find_if(string_double_map_.begin(), string_double_map_.end(), string_maps_to_double{ buffer_ });
+ if (it != string_double_map_.end())
+ {
+ more_ = visitor_->double_value(it->second, semantic_tag::none, *this, ec);
+ }
+ else if (column_index_ < column_types_.size() + offset_)
+ {
+ if (column_types_[column_index_ - offset_].col_type == csv_column_type::repeat_t)
+ {
+ offset_ = offset_ + column_types_[column_index_ - offset_].rep_count;
+ if (column_index_ - offset_ + 1 < column_types_.size())
+ {
+ if (column_index_ == offset_ || level_ > column_types_[column_index_-offset_].level)
+ {
+ more_ = visitor_->end_array(*this, ec);
+ }
+ level_ = column_index_ == offset_ ? 0 : column_types_[column_index_ - offset_].level;
+ }
+ }
+ if (level_ < column_types_[column_index_ - offset_].level)
+ {
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ level_ = column_types_[column_index_ - offset_].level;
+ }
+ else if (level_ > column_types_[column_index_ - offset_].level)
+ {
+ more_ = visitor_->end_array(*this, ec);
+ level_ = column_types_[column_index_ - offset_].level;
+ }
+ switch (column_types_[column_index_ - offset_].col_type)
+ {
+ case csv_column_type::integer_t:
+ {
+ std::basic_istringstream<CharT,std::char_traits<CharT>,char_allocator_type> iss{buffer_};
+ int64_t val;
+ iss >> val;
+ if (!iss.fail())
+ {
+ more_ = visitor_->int64_value(val, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ if (column_index_ - offset_ < column_defaults_.size() && column_defaults_[column_index_ - offset_].length() > 0)
+ {
+ basic_json_parser<CharT,temp_allocator_type> parser(alloc_);
+ parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length());
+ parser.parse_some(*visitor_);
+ parser.finish_parse(*visitor_);
+ }
+ else
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ }
+ }
+ break;
+ case csv_column_type::float_t:
+ {
+ if (options_.lossless_number())
+ {
+ more_ = visitor_->string_value(buffer_,semantic_tag::bigdec, *this, ec);
+ }
+ else
+ {
+ std::basic_istringstream<CharT, std::char_traits<CharT>, char_allocator_type> iss{ buffer_ };
+ double val;
+ iss >> val;
+ if (!iss.fail())
+ {
+ more_ = visitor_->double_value(val, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ if (column_index_ - offset_ < column_defaults_.size() && column_defaults_[column_index_ - offset_].length() > 0)
+ {
+ basic_json_parser<CharT,temp_allocator_type> parser(alloc_);
+ parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length());
+ parser.parse_some(*visitor_);
+ parser.finish_parse(*visitor_);
+ }
+ else
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ }
+ }
+ }
+ break;
+ case csv_column_type::boolean_t:
+ {
+ if (buffer_.length() == 1 && buffer_[0] == '0')
+ {
+ more_ = visitor_->bool_value(false, semantic_tag::none, *this, ec);
+ }
+ else if (buffer_.length() == 1 && buffer_[0] == '1')
+ {
+ more_ = visitor_->bool_value(true, semantic_tag::none, *this, ec);
+ }
+ else if (buffer_.length() == 5 && ((buffer_[0] == 'f' || buffer_[0] == 'F') && (buffer_[1] == 'a' || buffer_[1] == 'A') && (buffer_[2] == 'l' || buffer_[2] == 'L') && (buffer_[3] == 's' || buffer_[3] == 'S') && (buffer_[4] == 'e' || buffer_[4] == 'E')))
+ {
+ more_ = visitor_->bool_value(false, semantic_tag::none, *this, ec);
+ }
+ else if (buffer_.length() == 4 && ((buffer_[0] == 't' || buffer_[0] == 'T') && (buffer_[1] == 'r' || buffer_[1] == 'R') && (buffer_[2] == 'u' || buffer_[2] == 'U') && (buffer_[3] == 'e' || buffer_[3] == 'E')))
+ {
+ more_ = visitor_->bool_value(true, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ if (column_index_ - offset_ < column_defaults_.size() && column_defaults_[column_index_ - offset_].length() > 0)
+ {
+ basic_json_parser<CharT,temp_allocator_type> parser(alloc_);
+ parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length());
+ parser.parse_some(*visitor_);
+ parser.finish_parse(*visitor_);
+ }
+ else
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ }
+ }
+ break;
+ default:
+ if (buffer_.length() > 0)
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ if (column_index_ < column_defaults_.size() + offset_ && column_defaults_[column_index_ - offset_].length() > 0)
+ {
+ basic_json_parser<CharT,temp_allocator_type> parser(alloc_);
+ parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length());
+ parser.parse_some(*visitor_);
+ parser.finish_parse(*visitor_);
+ }
+ else
+ {
+ more_ = visitor_->string_value(string_view_type(), semantic_tag::none, *this, ec);
+ }
+ }
+ break;
+ }
+ }
+ else
+ {
+ if (infer_types)
+ {
+ end_value_with_numeric_check(ec);
+ }
+ else
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec);
+ }
+ }
+ }
+
+ enum class numeric_check_state
+ {
+ initial,
+ null,
+ boolean_true,
+ boolean_false,
+ minus,
+ zero,
+ integer,
+ fraction1,
+ fraction,
+ exp1,
+ exp,
+ not_a_number
+ };
+
+ /*
+ xxx_value
+ */
+ void end_value_with_numeric_check(std::error_code& ec)
+ {
+ numeric_check_state state = numeric_check_state::initial;
+ bool is_negative = false;
+ int precision = 0;
+ uint8_t decimal_places = 0;
+
+ auto last = buffer_.end();
+
+ std::string buffer;
+ for (auto p = buffer_.begin(); state != numeric_check_state::not_a_number && p != last; ++p)
+ {
+ switch (state)
+ {
+ case numeric_check_state::initial:
+ {
+ switch (*p)
+ {
+ case 'n':case 'N':
+ if ((last-p) == 4 && (p[1] == 'u' || p[1] == 'U') && (p[2] == 'l' || p[2] == 'L') && (p[3] == 'l' || p[3] == 'L'))
+ {
+ state = numeric_check_state::null;
+ }
+ else
+ {
+ state = numeric_check_state::not_a_number;
+ }
+ break;
+ case 't':case 'T':
+ if ((last-p) == 4 && (p[1] == 'r' || p[1] == 'R') && (p[2] == 'u' || p[2] == 'U') && (p[3] == 'e' || p[3] == 'U'))
+ {
+ state = numeric_check_state::boolean_true;
+ }
+ else
+ {
+ state = numeric_check_state::not_a_number;
+ }
+ break;
+ case 'f':case 'F':
+ if ((last-p) == 5 && (p[1] == 'a' || p[1] == 'A') && (p[2] == 'l' || p[2] == 'L') && (p[3] == 's' || p[3] == 'S') && (p[4] == 'e' || p[4] == 'E'))
+ {
+ state = numeric_check_state::boolean_false;
+ }
+ else
+ {
+ state = numeric_check_state::not_a_number;
+ }
+ break;
+ case '-':
+ is_negative = true;
+ buffer.push_back(*p);
+ state = numeric_check_state::minus;
+ break;
+ case '0':
+ ++precision;
+ buffer.push_back(*p);
+ state = numeric_check_state::zero;
+ break;
+ case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ buffer.push_back(*p);
+ state = numeric_check_state::integer;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::zero:
+ {
+ switch (*p)
+ {
+ case '.':
+ buffer.push_back(to_double_.get_decimal_point());
+ state = numeric_check_state::fraction1;
+ break;
+ case 'e':case 'E':
+ buffer.push_back(*p);
+ state = numeric_check_state::exp1;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::integer:
+ {
+ switch (*p)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ buffer.push_back(*p);
+ break;
+ case '.':
+ buffer.push_back(to_double_.get_decimal_point());
+ state = numeric_check_state::fraction1;
+ break;
+ case 'e':case 'E':
+ buffer.push_back(*p);
+ state = numeric_check_state::exp1;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::minus:
+ {
+ switch (*p)
+ {
+ case '0':
+ ++precision;
+ buffer.push_back(*p);
+ state = numeric_check_state::zero;
+ break;
+ case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ buffer.push_back(*p);
+ state = numeric_check_state::integer;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::fraction1:
+ {
+ switch (*p)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ ++decimal_places;
+ buffer.push_back(*p);
+ state = numeric_check_state::fraction;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::fraction:
+ {
+ switch (*p)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ ++decimal_places;
+ buffer.push_back(*p);
+ break;
+ case 'e':case 'E':
+ buffer.push_back(*p);
+ state = numeric_check_state::exp1;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::exp1:
+ {
+ switch (*p)
+ {
+ case '-':
+ buffer.push_back(*p);
+ break;
+ case '+':
+ break;
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ state = numeric_check_state::exp;
+ buffer.push_back(*p);
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::exp:
+ {
+ switch (*p)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ buffer.push_back(*p);
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ switch (state)
+ {
+ case numeric_check_state::null:
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ break;
+ case numeric_check_state::boolean_true:
+ more_ = visitor_->bool_value(true, semantic_tag::none, *this, ec);
+ break;
+ case numeric_check_state::boolean_false:
+ more_ = visitor_->bool_value(false, semantic_tag::none, *this, ec);
+ break;
+ case numeric_check_state::zero:
+ case numeric_check_state::integer:
+ {
+ if (is_negative)
+ {
+ int64_t val{ 0 };
+ auto result = jsoncons::detail::to_integer_decimal(buffer_.data(), buffer_.length(), val);
+ if (result)
+ {
+ more_ = visitor_->int64_value(val, semantic_tag::none, *this, ec);
+ }
+ else // Must be overflow
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::bigint, *this, ec);
+ }
+ }
+ else
+ {
+ uint64_t val{ 0 };
+ auto result = jsoncons::detail::to_integer_decimal(buffer_.data(), buffer_.length(), val);
+ if (result)
+ {
+ more_ = visitor_->uint64_value(val, semantic_tag::none, *this, ec);
+ }
+ else if (result.ec == jsoncons::detail::to_integer_errc::overflow)
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::bigint, *this, ec);
+ }
+ else
+ {
+ ec = result.ec;
+ more_ = false;
+ return;
+ }
+ }
+ break;
+ }
+ case numeric_check_state::fraction:
+ case numeric_check_state::exp:
+ {
+ if (options_.lossless_number())
+ {
+ more_ = visitor_->string_value(buffer_,semantic_tag::bigdec, *this, ec);
+ }
+ else
+ {
+ double d = to_double_(buffer.c_str(), buffer.length());
+ more_ = visitor_->double_value(d, semantic_tag::none, *this, ec);
+ }
+ break;
+ }
+ default:
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec);
+ break;
+ }
+ }
+ }
+
+ void push_state(csv_parse_state state)
+ {
+ state_stack_.push_back(state);
+ }
+
+ csv_parse_state pop_state()
+ {
+ JSONCONS_ASSERT(!state_stack_.empty())
+ csv_parse_state state = state_stack_.back();
+ state_stack_.pop_back();
+ return state;
+ }
+};
+
+using csv_parser = basic_csv_parser<char>;
+using wcsv_parser = basic_csv_parser<wchar_t>;
+
+}}
+
+#endif
+
diff --git a/include/jsoncons_ext/csv/csv_reader.hpp b/include/jsoncons_ext/csv/csv_reader.hpp
new file mode 100644
index 0000000..f10211a
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_reader.hpp
@@ -0,0 +1,348 @@
+// Copyright 2013 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_CSV_READER_HPP
+#define JSONCONS_CSV_CSV_READER_HPP
+
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <memory> // std::allocator
+#include <utility> // std::move
+#include <istream> // std::basic_istream
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons_ext/csv/csv_error.hpp>
+#include <jsoncons_ext/csv/csv_parser.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons/json_reader.hpp>
+#include <jsoncons/json_decoder.hpp>
+#include <jsoncons/source_adaptor.hpp>
+#include <jsoncons_ext/csv/csv_options.hpp>
+
+namespace jsoncons { namespace csv {
+
+ template<class CharT,class Source=jsoncons::stream_source<CharT>,class Allocator=std::allocator<char>>
+ class basic_csv_reader
+ {
+ struct stack_item
+ {
+ stack_item() noexcept
+ : array_begun_(false)
+ {
+ }
+
+ bool array_begun_;
+ };
+ using char_type = CharT;
+ using temp_allocator_type = Allocator;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT> char_allocator_type;
+
+ basic_csv_reader(const basic_csv_reader&) = delete;
+ basic_csv_reader& operator = (const basic_csv_reader&) = delete;
+
+ basic_default_json_visitor<CharT> default_visitor_;
+ text_source_adaptor<Source> source_;
+ basic_json_visitor<CharT>& visitor_;
+ basic_csv_parser<CharT,Allocator> parser_;
+
+ public:
+ // Structural characters
+ static constexpr size_t default_max_buffer_size = 16384;
+ //! Parse an input stream of CSV text into a json object
+ /*!
+ \param is The input stream to read from
+ */
+
+ template <class Sourceable>
+ basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const Allocator& alloc = Allocator())
+
+ : basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_csv_decode_options<CharT>(),
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+
+ : basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ options,
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_csv_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : source_(std::forward<Sourceable>(source)),
+ visitor_(visitor),
+ parser_(options, err_handler, alloc)
+
+ {
+ }
+
+ ~basic_csv_reader() noexcept = default;
+
+ void read()
+ {
+ std::error_code ec;
+ read(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read(std::error_code& ec)
+ {
+ read_internal(ec);
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+
+ bool eof() const
+ {
+ return parser_.source_exhausted() && source_.eof();
+ }
+
+ private:
+
+ void read_internal(std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = csv_errc::source_error;
+ return;
+ }
+ while (!parser_.stopped())
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ parser_.parse_some(visitor_, ec);
+ if (ec) return;
+ }
+ }
+ };
+
+ template<class CharT,class Source=jsoncons::stream_source<CharT>,class Allocator=std::allocator<char>>
+ class legacy_basic_csv_reader
+ {
+ struct stack_item
+ {
+ stack_item() noexcept
+ : array_begun_(false)
+ {
+ }
+
+ bool array_begun_;
+ };
+ using char_type = CharT;
+ using temp_allocator_type = Allocator;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT> char_allocator_type;
+
+ legacy_basic_csv_reader(const legacy_basic_csv_reader&) = delete;
+ legacy_basic_csv_reader& operator = (const legacy_basic_csv_reader&) = delete;
+
+ basic_default_json_visitor<CharT> default_visitor_;
+ text_source_adaptor<Source> source_;
+ basic_json_visitor<CharT>& visitor_;
+ basic_csv_parser<CharT,Allocator> parser_;
+
+ public:
+ // Structural characters
+ static constexpr size_t default_max_buffer_size = 16384;
+ //! Parse an input stream of CSV text into a json object
+ /*!
+ \param is The input stream to read from
+ */
+
+ template <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const Allocator& alloc = Allocator())
+
+ : legacy_basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_csv_decode_options<CharT>(),
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+
+ : legacy_basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ options,
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : legacy_basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_csv_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(std::forward<Sourceable>(source)),
+ visitor_(visitor),
+ parser_(options, err_handler, alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(),
+ visitor_(visitor),
+ parser_(options, err_handler, alloc)
+ {
+ jsoncons::basic_string_view<CharT> sv(std::forward<Sourceable>(source));
+ auto r = unicode_traits::detect_encoding_from_bom(sv.data(), sv.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column()));
+ }
+ std::size_t offset = (r.ptr - sv.data());
+ parser_.update(sv.data()+offset,sv.size()-offset);
+ }
+
+ ~legacy_basic_csv_reader() noexcept = default;
+
+ void read()
+ {
+ std::error_code ec;
+ read(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read(std::error_code& ec)
+ {
+ read_internal(ec);
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+
+ bool eof() const
+ {
+ return parser_.source_exhausted() && source_.eof();
+ }
+
+ private:
+
+ void read_internal(std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = csv_errc::source_error;
+ return;
+ }
+ while (!parser_.stopped())
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ parser_.parse_some(visitor_, ec);
+ if (ec) return;
+ }
+ }
+ };
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ using csv_reader = legacy_basic_csv_reader<char>;
+ using wcsv_reader = legacy_basic_csv_reader<wchar_t>;
+#endif
+
+ using csv_string_reader = basic_csv_reader<char,string_source<char>>;
+ using wcsv_string_reader = basic_csv_reader<wchar_t,string_source<wchar_t>>;
+ using csv_stream_reader = basic_csv_reader<char,stream_source<char>>;
+ using wcsv_stream_reader = basic_csv_reader<wchar_t,stream_source<wchar_t>>;
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/csv/csv_serializer.hpp b/include/jsoncons_ext/csv/csv_serializer.hpp
new file mode 100644
index 0000000..ec73510
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_serializer.hpp
@@ -0,0 +1,12 @@
+// Copyright 2013 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_CSV_SERIALIZER_HPP
+#define JSONCONS_CSV_CSV_SERIALIZER_HPP
+
+#include <jsoncons_ext/csv/csv_encoder.hpp>
+
+#endif
diff --git a/include/jsoncons_ext/csv/decode_csv.hpp b/include/jsoncons_ext/csv/decode_csv.hpp
new file mode 100644
index 0000000..b91c58b
--- /dev/null
+++ b/include/jsoncons_ext/csv/decode_csv.hpp
@@ -0,0 +1,208 @@
+/// Copyright 2013 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_DECODE_CSV_HPP
+#define JSONCONS_CSV_DECODE_CSV_HPP
+
+#include <jsoncons_ext/csv/csv_options.hpp>
+#include <jsoncons_ext/csv/csv_reader.hpp>
+#include <jsoncons_ext/csv/csv_encoder.hpp>
+#include <jsoncons_ext/csv/csv_cursor.hpp>
+
+namespace jsoncons {
+namespace csv {
+
+ template <class T,class Source>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_sequence_of<Source,typename T::char_type>::value,T>::type
+ decode_csv(const Source& s, const basic_csv_decode_options<typename Source::value_type>& options = basic_csv_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ json_decoder<T> decoder;
+
+ basic_csv_reader<char_type,jsoncons::string_source<char_type>> reader(s,decoder,options);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template <class T,class Source>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_char_sequence<Source>::value,T>::type
+ decode_csv(const Source& s, const basic_csv_decode_options<typename Source::value_type>& options = basic_csv_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ basic_csv_cursor<char_type> cursor(s, options);
+ jsoncons::json_decoder<basic_json<char_type>> decoder;
+
+ std::error_code ec;
+ T val = decode_traits<T,char_type>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template <class T,class CharT>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(std::basic_istream<CharT>& is, const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>())
+ {
+ using char_type = CharT;
+
+ json_decoder<T> decoder;
+
+ basic_csv_reader<char_type,jsoncons::stream_source<char_type>> reader(is,decoder,options);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template <class T,class CharT>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(std::basic_istream<CharT>& is, const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>())
+ {
+ basic_csv_cursor<CharT> cursor(is, options);
+ jsoncons::json_decoder<basic_json<CharT>> decoder;
+
+ std::error_code ec;
+ T val = decode_traits<T,CharT>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template <class T, class InputIt>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(InputIt first, InputIt last,
+ const basic_csv_decode_options<typename std::iterator_traits<InputIt>::value_type>& options =
+ basic_csv_decode_options<typename std::iterator_traits<InputIt>::value_type>())
+ {
+ using char_type = typename std::iterator_traits<InputIt>::value_type;
+
+ jsoncons::json_decoder<T> decoder;
+ basic_csv_reader<char_type, iterator_source<InputIt>> reader(iterator_source<InputIt>(first,last), decoder, options);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template <class T, class InputIt>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(InputIt first, InputIt last,
+ const basic_csv_decode_options<typename std::iterator_traits<InputIt>::value_type>& options =
+ basic_csv_decode_options<typename std::iterator_traits<InputIt>::value_type>())
+ {
+ using char_type = typename std::iterator_traits<InputIt>::value_type;
+
+ basic_csv_cursor<char_type,iterator_source<InputIt>> cursor(iterator_source<InputIt>(first, last), options);
+ jsoncons::json_decoder<basic_json<char_type>> decoder;
+ std::error_code ec;
+ T val = decode_traits<T,char_type>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ // With leading allocator parameter
+
+ template <class T,class Source,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_sequence_of<Source,typename T::char_type>::value,T>::type
+ decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& s,
+ const basic_csv_decode_options<typename Source::value_type>& options = basic_csv_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+
+ basic_csv_reader<char_type,jsoncons::string_source<char_type>,TempAllocator> reader(s,decoder,options,temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template <class T,class Source,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_char_sequence<Source>::value,T>::type
+ decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& s,
+ const basic_csv_decode_options<typename Source::value_type>& options = basic_csv_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ basic_csv_cursor<char_type,stream_source<char_type>,TempAllocator> cursor(s, options, temp_alloc);
+ json_decoder<basic_json<char_type,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char_type>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template <class T,class CharT,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::basic_istream<CharT>& is,
+ const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>())
+ {
+ using char_type = CharT;
+
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+
+ basic_csv_reader<char_type,jsoncons::string_source<char_type>,TempAllocator> reader(is,decoder,options,temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template <class T,class CharT,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::basic_istream<CharT>& is,
+ const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>())
+ {
+ basic_csv_cursor<CharT,stream_source<CharT>,TempAllocator> cursor(is, options, temp_alloc);
+ json_decoder<basic_json<CharT,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,CharT>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+} // namespace csv
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/csv/encode_csv.hpp b/include/jsoncons_ext/csv/encode_csv.hpp
new file mode 100644
index 0000000..d919253
--- /dev/null
+++ b/include/jsoncons_ext/csv/encode_csv.hpp
@@ -0,0 +1,122 @@
+/// Copyright 2013 Daniel Parker
+// Distributed under the Boost license, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_ENCODE_CSV_HPP
+#define JSONCONS_CSV_ENCODE_CSV_HPP
+
+#include <jsoncons_ext/csv/csv_options.hpp>
+#include <jsoncons_ext/csv/csv_reader.hpp>
+#include <jsoncons_ext/csv/csv_encoder.hpp>
+
+namespace jsoncons {
+namespace csv {
+
+ template <class T,class Container>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_csv(const T& j, Container& s, const basic_csv_encode_options<typename Container::value_type>& options = basic_csv_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+ basic_csv_encoder<char_type,jsoncons::string_sink<std::basic_string<char_type>>> encoder(s,options);
+ j.dump(encoder);
+ }
+
+ template <class T,class Container>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_csv(const T& val, Container& s, const basic_csv_encode_options<typename Container::value_type>& options = basic_csv_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+ basic_csv_encoder<char_type,jsoncons::string_sink<std::basic_string<char_type>>> encoder(s,options);
+ std::error_code ec;
+ encode_traits<T,char_type>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template <class T, class CharT>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type
+ encode_csv(const T& j, std::basic_ostream<CharT>& os, const basic_csv_encode_options<CharT>& options = basic_csv_encode_options<CharT>())
+ {
+ using char_type = CharT;
+ basic_csv_encoder<char_type,jsoncons::stream_sink<char_type>> encoder(os,options);
+ j.dump(encoder);
+ }
+
+ template <class T, class CharT>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type
+ encode_csv(const T& val, std::basic_ostream<CharT>& os, const basic_csv_encode_options<CharT>& options = basic_csv_encode_options<CharT>())
+ {
+ using char_type = CharT;
+ basic_csv_encoder<char_type,jsoncons::stream_sink<char_type>> encoder(os,options);
+ std::error_code ec;
+ encode_traits<T,CharT>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // with temp_allocator_arg_t
+
+ template <class T, class Container, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j, Container& s, const basic_csv_encode_options<typename Container::value_type>& options = basic_csv_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+ basic_csv_encoder<char_type,jsoncons::string_sink<std::basic_string<char_type>>,TempAllocator> encoder(s, options, temp_alloc);
+ j.dump(encoder);
+ }
+
+ template <class T, class Container, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val, Container& s, const basic_csv_encode_options<typename Container::value_type>& options = basic_csv_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+ basic_csv_encoder<char_type,jsoncons::string_sink<std::basic_string<char_type>>,TempAllocator> encoder(s, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,char_type>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template <class T, class CharT, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type
+ encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j, std::basic_ostream<CharT>& os, const basic_csv_encode_options<CharT>& options = basic_csv_encode_options<CharT>())
+ {
+ using char_type = CharT;
+ basic_csv_encoder<char_type,jsoncons::stream_sink<char_type>,TempAllocator> encoder(os, options, temp_alloc);
+ j.dump(encoder);
+ }
+
+ template <class T, class CharT, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type
+ encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val, std::basic_ostream<CharT>& os, const basic_csv_encode_options<CharT>& options = basic_csv_encode_options<CharT>())
+ {
+ using char_type = CharT;
+ basic_csv_encoder<char_type,jsoncons::stream_sink<char_type>,TempAllocator> encoder(os, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,CharT>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+} // namespace csv
+} // namespace jsoncons
+
+#endif