Skip to content

Instantly share code, notes, and snippets.

@pyrtsa
Created March 12, 2012 10:48
Show Gist options
  • Save pyrtsa/2021128 to your computer and use it in GitHub Desktop.
Save pyrtsa/2021128 to your computer and use it in GitHub Desktop.
// The following code is in public domain. Re-releases are free to choose
// whether to attribute the code to its original author or not.
// Author: Pyry Jahkola <pyry.jahkola@iki.fi>
#include <sstream>
#include <iostream>
#include <map>
#include <cassert>
#include <stack>
#include <vector>
namespace json {
// -----------------------------------------------------------------------------
template <typename UnicodeIter>
std::ostream & to_utf8(std::ostream & os, UnicodeIter begin, UnicodeIter end) {
using std::uint8_t;
using std::uint32_t;
for (UnicodeIter i = begin; i != end; i++) {
uint32_t u = *i; // next Unicode code point
if (u < 0x80) {
os << static_cast<uint8_t>(u);
} else if (u < 0x800) {
os << static_cast<uint8_t>(0xC0 | (u >> 6))
<< static_cast<uint8_t>(0x80 | (u & 0x3F));
} else if (u < 0x10000) {
os << static_cast<uint8_t>(0xE0 | (u >> 12))
<< static_cast<uint8_t>(0x80 | ((u >> 6) & 0x3F))
<< static_cast<uint8_t>(0x80 | (u & 0x3F));
} else if (u < 0x110000) {
os << static_cast<uint8_t>(0xF0 | (u >> 18))
<< static_cast<uint8_t>(0x80 | ((u >> 12) & 0x3F))
<< static_cast<uint8_t>(0x80 | ((u >> 6) & 0x3F))
<< static_cast<uint8_t>(0x80 | (u & 0x3F));
} else {
assert(false && "invalid Unicode value!");
// U+FFFD, http://en.wikipedia.org/wiki/Replacement_character
os << "\xEF\xBF\xBD";
}
}
return os;
}
template <typename UnicodeIter>
std::string to_utf8(UnicodeIter begin, UnicodeIter end) {
std::ostringstream os;
to_utf8(os, begin, end);
return os.str();
}
template <typename C, typename T>
std::ostream & to_utf8(std::ostream & os,
std::basic_string<C, T> const & unicode_string)
{
return to_utf8(os, unicode_string.begin(), unicode_string.end());
}
template <typename C, typename T>
std::string to_utf8(std::basic_string<C, T> const & unicode_string) {
return to_utf8(unicode_string.begin(), unicode_string.end());
}
// -----------------------------------------------------------------------------
std::ostream & quote(std::ostream & os, std::string const & utf8) {
typedef std::string::const_iterator iter;
char const hexa[] = "0123456789abcdef";
os << '"';
for (iter i = utf8.begin(), e = utf8.end(); i != e; i++) {
unsigned char const c = static_cast<unsigned char>(*i);
if (c < 0x20) {
if (c == '\n') os << "\\n";
else if (c == '\r') os << "\\r";
else os << "\\u00" << hexa[c >> 4] << hexa[c & 0xf];
} else if (c == '"') {
os << "\\\"";
} else if (c == '\\') {
os << "\\\\";
} else {
os << c;
}
}
return os << '"';
}
std::string quote(std::string const & s) {
std::ostringstream o;
json::quote(o, s);
return o.str();
}
namespace tag {
struct object {};
struct array {};
struct end {};
struct null {};
}
extern tag::object const object;
extern tag::array const array;
extern tag::end const end;
extern tag::null const null;
template <typename Range, typename Enable=void>
struct range_const_iterator {};
template <typename Range>
struct range_const_iterator<Range> {
typedef typename Range::const_iterator type;
};
template <typename T, std::size_t N> struct range_const_iterator<T[N]> {
typedef T const * type;
};
template <typename T, std::size_t N> struct range_const_iterator<T(&)[N]> {
typedef T const * type;
};
template <typename Range>
typename range_const_iterator<Range>::type cbegin(Range const & r) {
return r.begin();
}
template <typename Range>
typename range_const_iterator<Range>::type cend(Range const & r) {
return r.end();
}
template <typename T, std::size_t N>
inline T const * cbegin(T (&r)[N]) { return r; }
template <typename T, std::size_t N>
inline T const * cend(T (&r)[N]) { return cbegin(r) + N; }
template <typename T, std::size_t N>
inline T const * cbegin(T const (&r)[N]) { return r; }
template <typename T, std::size_t N>
inline T const * cend(T const (&r)[N]) { return cbegin(r) + N; }
template <bool Cond, typename Then=void> struct enable_if {};
template <typename Then> struct enable_if<true, Then> { typedef Then type; };
namespace detail {
struct yes { char _[1]; };
struct nay { char _[2]; };
template <typename T, typename=typename T::const_iterator>
struct is_range_helper;
template <typename T> yes is_range_test(is_range_helper<T> *);
template <typename T> nay is_range_test(...);
}
template <bool C> struct bool_c {
typedef bool type;
static bool const value = C;
};
template <typename T> struct is_range
: bool_c<sizeof(detail::is_range_test<T>(0)) == sizeof(detail::yes)> {};
template <typename T> struct is_range<T &> : is_range<T> {};
template <typename T> struct is_range<T const> : is_range<T> {};
template <typename T, std::size_t N> struct is_range<T(&)[N]> : bool_c<true> {};
template <typename T> struct is_number : bool_c<false> {};
template <> struct is_number<char> : bool_c<true> {};
template <> struct is_number<signed char> : bool_c<true> {};
template <> struct is_number<unsigned char> : bool_c<true> {};
template <> struct is_number<short> : bool_c<true> {};
template <> struct is_number<int> : bool_c<true> {};
template <> struct is_number<long> : bool_c<true> {};
template <> struct is_number<long long> : bool_c<true> {};
template <> struct is_number<unsigned short> : bool_c<true> {};
template <> struct is_number<unsigned int> : bool_c<true> {};
template <> struct is_number<unsigned long> : bool_c<true> {};
template <> struct is_number<unsigned long long> : bool_c<true> {};
// object ::= '{' (string ':' value (',' string ':' value)*)? '}'
// array ::= '[' (value (',' value)*)? ']'
// value ::= string | number | object | array | true | false | null
class ostream {
public:
enum state { ROOT=1, OBJECT=2, KEY=4, VALUE=8, ARRAY=16, ELEMENT=32 };
private:
std::shared_ptr<std::ostringstream> p;
std::ostream & os;
std::stack<state> stack;
std::ostream & append_os() {
if (in_state(KEY | ELEMENT)) os << ',';
else if (in_state(VALUE)) os << ':';
if (in_state(OBJECT | KEY)) stack.top() = VALUE;
else if (in_state(VALUE)) stack.top() = KEY;
else if (in_state(ARRAY)) stack.top() = ELEMENT;
return os;
}
public:
explicit ostream() : p(new std::ostringstream), os(*p) { stack.push(ROOT); }
explicit ostream(std::ostream & os) : os(os) { stack.push(ROOT); }
bool in_state(int mask) const { return stack.size() && stack.top() & mask; }
bool finished() const { return stack.empty(); }
std::string str() const {
assert(finished() && p);
return p ? p->str() : "";
}
operator std::string() const { return str(); }
ostream & operator<<(tag::object) {
assert(in_state(ROOT | VALUE | ARRAY | ELEMENT));
append_os() << '{';
stack.push(OBJECT);
return *this;
}
ostream & operator<<(tag::array) {
assert(in_state(VALUE | ARRAY | ELEMENT));
append_os() << '[';
stack.push(ARRAY);
return *this;
}
ostream & operator<<(tag::end) {
assert(in_state(OBJECT | KEY | ARRAY | ELEMENT));
os << (in_state(OBJECT | KEY) ? '}' : ']');
stack.pop();
if (in_state(ROOT)) {
stack.pop(); // finish if we ended up at root level
assert(finished());
} else if (in_state(VALUE)) {
stack.top() = KEY;
}
return *this;
}
ostream & operator<<(char const * string) {
return *this << std::string(string);
}
ostream & operator<<(std::string const & string) {
assert(in_state(OBJECT | KEY | VALUE | ARRAY | ELEMENT));
quote(append_os(), string);
return *this;
}
ostream & operator<<(bool value) {
assert(in_state(VALUE | ARRAY | ELEMENT));
append_os() << (value ? "true" : "false");
return *this;
}
ostream & operator<<(tag::null) {
assert(in_state(VALUE | ARRAY | ELEMENT));
append_os() << "null";
return *this;
}
template <typename T>
typename enable_if<is_number<T>::value, ostream &>::type
operator<<(T number) {
assert(in_state(VALUE | ARRAY | ELEMENT));
append_os() << static_cast<double>(number);
return *this;
}
template <typename K, typename V>
ostream & operator<<(std::map<K, V> const & object) {
typedef typename std::map<K, V>::const_iterator iter;
assert(in_state(VALUE | ARRAY | ELEMENT));
*this << tag::object();
for (iter i = object.begin(), e = object.end(); i != e; i++)
*this << i->first << i->second;
return *this << tag::end();
}
template <typename Range>
typename enable_if<is_range<Range>::value, ostream &>::type
operator<<(Range const & array) {
using std::begin; using std::end;
typedef typename range_const_iterator<Range>::type iter;
assert(in_state(VALUE | ARRAY | ELEMENT));
*this << tag::array();
for (iter i = begin(array), e = end(array); i != e; i++)
*this << *i;
return *this << tag::end();
}
template <typename Element, std::size_t N>
ostream & operator<<(Element const (&array)[N]) {
using std::begin; using std::end;
typedef Element const * iter;
assert(in_state(VALUE | ARRAY | ELEMENT));
*this << tag::array();
for (iter i = begin(array), e = end(array); i != e; i++)
*this << *i;
return *this << tag::end();
}
};
} // json
// =============================================================================
std::string example1() {
return json::quote("Abc. \"123 \\\1 \xE2\x80\xA6\r\n");
}
std::string example2() {
char const * things[3] = { "pen", "brush", "hammer" };
std::vector<std::string> numbers;
numbers.push_back("one");
numbers.push_back("two");
numbers.push_back("three");
using json::object;
using json::array;
using json::null;
using json::end;
return json::ostream()
<< object
<< "name" << "Hello World!"
<< "year" << 2012
<< "things" << things
<< "list" << array
<< true
<< false
<< 2
<< null
<< 4
<< "fi\\ve"
<< object
<< "first" << "Pat"
<< "second" << "Matt"
<< "numbers" << numbers
<< end
<< end
<< "empty\tobject" << object << end
<< "empty\r\narray" << array << end
<< "array of empty arrays etc." << array
<< array << end
<< array << end
<< array << end
<< array << array << array << array << end << end << end << end
<< end
<< "del" "\x7F" ", huh?" << "\xE2\x80\xA6"
<< "2 \\0 bytes" << std::string(2, '\0')
<< end;
}
int main() {
std::cout << example2() << std::endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment