Thrift: Add a full-featured JSON protocol for C++.

Summary:
This change adds a new and exciting protocol to Thrift.  It uses
RFC-compliant JSON as the wire protocol and is fully human readable.
(once a little whitespace has been inserted.)  Unlike the existing
JSON protocol for Java, which is intended to allow Thrift data to be
transferred to scripting languages, this protocol is lossless and fully
read-write.  It was written by Chad Walters of Powerset and reviewed
by David Reiss.

Tested by running make check.


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665482 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/src/protocol/TJSONProtocol.cpp b/lib/cpp/src/protocol/TJSONProtocol.cpp
new file mode 100644
index 0000000..179a178
--- /dev/null
+++ b/lib/cpp/src/protocol/TJSONProtocol.cpp
@@ -0,0 +1,995 @@
+// Copyright (c) 2006- Facebook
+// Distributed under the Thrift Software License
+//
+// See accompanying file LICENSE or visit the Thrift site at:
+// http://developers.facebook.com/thrift/
+
+#include "TJSONProtocol.h"
+
+#include <math.h>
+#include "TBase64Utils.h"
+#include <transport/TTransportException.h>
+
+using namespace facebook::thrift::transport;
+
+namespace facebook { namespace thrift { namespace protocol {
+
+
+// Static data
+
+static const uint8_t kJSONObjectStart = '{';
+static const uint8_t kJSONObjectEnd = '}';
+static const uint8_t kJSONArrayStart = '[';
+static const uint8_t kJSONArrayEnd = ']';
+static const uint8_t kJSONNewline = '\n';
+static const uint8_t kJSONPairSeparator = ':';
+static const uint8_t kJSONElemSeparator = ',';
+static const uint8_t kJSONBackslash = '\\';
+static const uint8_t kJSONStringDelimiter = '"';
+static const uint8_t kJSONZeroChar = '0';
+static const uint8_t kJSONEscapeChar = 'u';
+
+static const std::string kJSONEscapePrefix("\\u00");
+
+static const uint8_t kThriftVersion1 = 1;
+
+static const std::string kThriftNan("NaN");
+static const std::string kThriftInfinity("Infinity");
+static const std::string kThriftNegativeInfinity("-Infinity");
+
+static const std::string kTypeNameBool("tf");
+static const std::string kTypeNameByte("i8");
+static const std::string kTypeNameI16("i16");
+static const std::string kTypeNameI32("i32");
+static const std::string kTypeNameI64("i64");
+static const std::string kTypeNameDouble("dbl");
+static const std::string kTypeNameStruct("rec");
+static const std::string kTypeNameString("str");
+static const std::string kTypeNameMap("map");
+static const std::string kTypeNameList("lst");
+static const std::string kTypeNameSet("set");
+
+static const std::string &getTypeNameForTypeID(TType typeID) {
+  switch (typeID) {
+  case T_BOOL:
+    return kTypeNameBool;
+  case T_BYTE:
+    return kTypeNameByte;
+  case T_I16:
+    return kTypeNameI16;
+  case T_I32:
+    return kTypeNameI32;
+  case T_I64:
+    return kTypeNameI64;
+  case T_DOUBLE:
+    return kTypeNameDouble;
+  case T_STRING:
+    return kTypeNameString;
+  case T_STRUCT:
+    return kTypeNameStruct;
+  case T_MAP:
+    return kTypeNameMap;
+  case T_SET:
+    return kTypeNameSet;
+  case T_LIST:
+    return kTypeNameList;
+  default:
+    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+                             "Unrecognized type");
+  }
+}
+
+static TType getTypeIDForTypeName(const std::string &name) {
+  TType result = T_STOP; // Sentinel value
+  switch (name[0]) {
+  case 'd':
+    result = T_DOUBLE;
+    break;
+  case 'i':
+    switch (name[1]) {
+    case '8':
+      result = T_BYTE;
+      break;
+    case '1':
+      result = T_I16;
+      break;
+    case '3':
+      result = T_I32;
+      break;
+    case '6':
+      result = T_I64;
+      break;
+    }
+    break;
+  case 'l':
+    result = T_LIST;
+    break;
+  case 'm':
+    result = T_MAP;
+    break;
+  case 'r':
+    result = T_STRUCT;
+    break;
+  case 's':
+    if (name[1] == 't') {
+      result = T_STRING;
+    }
+    else if (name[1] == 'e') {
+      result = T_SET;
+    }
+    break;
+  case 't':
+    result = T_BOOL;
+    break;
+  }
+  if (result == T_STOP) {
+    throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
+                             "Unrecognized type");
+  }
+  return result;
+}
+
+
+// This table describes the handling for the first 0x30 characters
+//  0 : escape using "\u00xx" notation
+//  1 : just output index
+// <other> : escape using "\<other>" notation
+static const uint8_t kJSONCharTable[0x30] = {
+//  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
+    0,  0,  0,  0,  0,  0,  0,  0,'b','t','n',  0,'f','r',  0,  0, // 0
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 1
+    1,  1,'"',  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, // 2
+};
+
+
+// This string's characters must match up with the elements in kEscapeCharVals.
+// I don't have '/' on this list even though it appears on www.json.org --
+// it is not in the RFC
+const static std::string kEscapeChars("\"\\bfnrt");
+
+// The elements of this array must match up with the sequence of characters in
+// kEscapeChars
+const static uint8_t kEscapeCharVals[7] = {
+  '"', '\\', '\b', '\f', '\n', '\r', '\t',
+};
+
+
+// Static helper functions
+
+// Read 1 character from the transport trans and verify that it is the
+// expected character ch.
+// Throw a protocol exception if it is not.
+static uint32_t readSyntaxChar(TTransport &trans, uint8_t ch) {
+  uint8_t b[1];
+  trans.readAll(b, 1);
+  if (b[0] != ch) {
+    throw TProtocolException(TProtocolException::INVALID_DATA,
+                             "Expected \'" + std::string((char *)&ch, 1) +
+                             "\'; got \'" + std::string((char *)b, 1) +
+                             "\'.");
+  }
+  return 1;
+}
+
+// Borrow 1 byte from the transport trans and return the value read
+// Throw a transport exception if the byte cannot be borrowed
+static uint8_t borrowByte(TTransport &trans) {
+  uint8_t b[1];
+  uint32_t len = 1;
+  const uint8_t *buf =  trans.borrow(b, &len);
+  if (!buf || !len) {
+    throw TTransportException(TTransportException::UNKNOWN,
+                              "Could not borrow 1 byte from transport.");
+  }
+  return *buf;
+}
+
+// Return the integer value of a hex character ch.
+// Throw a protocol exception if the character is not [0-9a-f].
+static uint8_t hexVal(uint8_t ch) {
+  if ((ch >= '0') && (ch <= '9')) {
+    return ch - '0';
+  }
+  else if ((ch >= 'a') && (ch <= 'f')) {
+    return ch - 'a';
+  }
+  else {
+    throw TProtocolException(TProtocolException::INVALID_DATA,
+                             "Expected hex val ([0-9a-f]); got \'"
+                               + std::string((char *)&ch, 1) + "\'.");
+  }
+}
+
+// Return the hex character representing the integer val. The value is masked
+// to make sure it is in the correct range.
+static uint8_t hexChar(uint8_t val) {
+  val &= 0x0F;
+  if (val < 10) {
+    return val + '0';
+  }
+  else {
+    return val + 'a';
+  }
+}
+
+// Return true if the character ch is in [-+0-9.Ee]; false otherwise
+static bool isJSONNumeric(uint8_t ch) {
+  switch (ch) {
+  case '+':
+  case '-':
+  case '.':
+  case '0':
+  case '1':
+  case '2':
+  case '3':
+  case '4':
+  case '5':
+  case '6':
+  case '7':
+  case '8':
+  case '9':
+  case 'E':
+  case 'e':
+    return true;
+  }
+  return false;
+}
+
+
+/**
+ * Class to serve as base JSON context and base class for other context
+ * implementations
+ */
+class TJSONContext {
+
+ public:
+
+  TJSONContext() {};
+
+  virtual ~TJSONContext() {};
+
+  /**
+   * Write context data to the transport. Default is to do nothing.
+   */
+  virtual uint32_t write(TTransport &trans) {
+    return 0;
+  };
+
+  /**
+   * Read context data from the transport. Default is to do nothing.
+   */
+  virtual uint32_t read(TTransport &trans) {
+    return 0;
+  };
+
+  /**
+   * Return true if numbers need to be escaped as strings in this context.
+   * Default behavior is to return false.
+   */
+  virtual bool escapeNum() {
+    return false;
+  }
+};
+
+// Context class for object member key-value pairs
+class JSONPairContext : public TJSONContext {
+
+public:
+
+  JSONPairContext() :
+    first_(true),
+    colon_(true) {
+  }
+
+  uint32_t write(TTransport &trans) {
+    if (first_) {
+      first_ = false;
+      colon_ = true;
+      return 0;
+    }
+    else {
+      trans.write(colon_ ? &kJSONPairSeparator : &kJSONElemSeparator, 1);
+      colon_ = !colon_;
+      return 1;
+    }
+  }
+
+  uint32_t read(TTransport &trans) {
+    if (first_) {
+      first_ = false;
+      colon_ = true;
+      return 0;
+    }
+    else {
+      uint8_t ch = (colon_ ? kJSONPairSeparator : kJSONElemSeparator);
+      colon_ = !colon_;
+      return readSyntaxChar(trans, ch);
+    }
+  }
+
+  // Numbers must be turned into strings if they are the key part of a pair
+  virtual bool escapeNum() {
+    return colon_;
+  }
+
+  private:
+
+    bool first_;
+    bool colon_;
+};
+
+// Context class for lists
+class JSONListContext : public TJSONContext {
+
+public:
+
+  JSONListContext() :
+    first_(true) {
+  }
+
+  uint32_t write(TTransport &trans) {
+    if (first_) {
+      first_ = false;
+      return 0;
+    }
+    else {
+      trans.write(&kJSONElemSeparator, 1);
+      return 1;
+    }
+  }
+
+  uint32_t read(TTransport &trans) {
+    if (first_) {
+      first_ = false;
+      return 0;
+    }
+    else {
+      return readSyntaxChar(trans, kJSONElemSeparator);
+    }
+  }
+
+  private:
+    bool first_;
+};
+
+
+TJSONProtocol::TJSONProtocol(boost::shared_ptr<TTransport> ptrans) :
+  TProtocol(ptrans),
+  context_(new TJSONContext()) {
+}
+
+TJSONProtocol::~TJSONProtocol() {}
+
+void TJSONProtocol::pushContext(boost::shared_ptr<TJSONContext> c) {
+  contexts_.push(context_);
+  context_ = c;
+}
+
+void TJSONProtocol::popContext() {
+  context_ = contexts_.top();
+  contexts_.pop();
+}
+
+// Write the character ch as a JSON escape sequence ("\u00xx")
+uint32_t TJSONProtocol::writeJSONEscapeChar(uint8_t ch) {
+  trans_->write((const uint8_t *)kJSONEscapePrefix.c_str(),
+                kJSONEscapePrefix.length());
+  uint8_t outCh = hexChar(ch >> 4);
+  trans_->write(&outCh, 1);
+  outCh = hexChar(ch);
+  trans_->write(&outCh, 1);
+  return 6;
+}
+
+// Write the character ch as part of a JSON string, escaping as appropriate.
+uint32_t TJSONProtocol::writeJSONChar(uint8_t ch) {
+  if (ch >= 0x30) {
+    if (ch == kJSONBackslash) { // Only special character >= 0x30 is '\'
+      trans_->write(&kJSONBackslash, 1);
+      trans_->write(&kJSONBackslash, 1);
+      return 2;
+    }
+    else {
+      trans_->write(&ch, 1);
+      return 1;
+    }
+  }
+  else {
+    uint8_t outCh = kJSONCharTable[ch];
+    // Check if regular character, backslash escaped, or JSON escaped
+    if (outCh == 1) {
+      trans_->write(&ch, 1);
+      return 1;
+    }
+    else if (outCh > 1) {
+      trans_->write(&kJSONBackslash, 1);
+      trans_->write(&outCh, 1);
+      return 2;
+    }
+    else {
+      return writeJSONEscapeChar(ch);
+    }
+  }
+}
+
+// Write out the contents of the string str as a JSON string, escaping
+// characters as appropriate.
+uint32_t TJSONProtocol::writeJSONString(const std::string &str) {
+  uint32_t result = context_->write(*trans_);
+  result += 2; // For quotes
+  trans_->write(&kJSONStringDelimiter, 1);
+  std::string::const_iterator iter(str.begin());
+  std::string::const_iterator end(str.end());
+  while (iter != end) {
+    result += writeJSONChar(*iter++);
+  }
+  trans_->write(&kJSONStringDelimiter, 1);
+  return result;
+}
+
+// Write out the contents of the string as JSON string, base64-encoding
+// the string's contents, and escaping as appropriate
+uint32_t TJSONProtocol::writeJSONBase64(const std::string &str) {
+  uint32_t result = context_->write(*trans_);
+  result += 2; // For quotes
+  trans_->write(&kJSONStringDelimiter, 1);
+  uint8_t b[4];
+  const uint8_t *bytes = (const uint8_t *)str.c_str();
+  uint32_t len = str.length();
+  while (len >= 3) {
+    // Encode 3 bytes at a time
+    base64_encode(bytes, 3, b);
+    trans_->write(b, 4);
+    result += 4;
+    bytes += 3;
+    len -=3;
+  }
+  if (len) { // Handle remainder
+    base64_encode(bytes, len, b);
+    trans_->write(b, len + 1);
+    result += len + 1;
+  }
+  trans_->write(&kJSONStringDelimiter, 1);
+  return result;
+}
+
+// Convert the given integer type to a JSON number, or a string
+// if the context requires it (eg: key in a map pair).
+template <typename NumberType>
+uint32_t TJSONProtocol::writeJSONInteger(NumberType num) {
+  uint32_t result = context_->write(*trans_);
+  std::string val(boost::lexical_cast<std::string>(num));
+  bool escapeNum = context_->escapeNum();
+  if (escapeNum) {
+    trans_->write(&kJSONStringDelimiter, 1);
+    result += 1;
+  }
+  trans_->write((const uint8_t *)val.c_str(), val.length());
+  result += val.length();
+  if (escapeNum) {
+    trans_->write(&kJSONStringDelimiter, 1);
+    result += 1;
+  }
+  return result;
+}
+
+// Convert the given double to a JSON string, which is either the number,
+// "NaN" or "Infinity" or "-Infinity".
+uint32_t TJSONProtocol::writeJSONDouble(double num) {
+  uint32_t result = context_->write(*trans_);
+  std::string val(boost::lexical_cast<std::string>(num));
+
+  // Normalize output of boost::lexical_cast for NaNs and Infinities
+  bool special = false;
+  switch (val[0]) {
+  case 'N':
+  case 'n':
+    val = kThriftNan;
+    special = true;
+    break;
+  case 'I':
+  case 'i':
+    val = kThriftInfinity;
+    special = true;
+    break;
+  case '-':
+    if ((val[1] == 'I') || (val[1] == 'i')) {
+      val = kThriftNegativeInfinity;
+      special = true;
+    }
+    break;
+  }
+
+  bool escapeNum = special || context_->escapeNum();
+  if (escapeNum) {
+    trans_->write(&kJSONStringDelimiter, 1);
+    result += 1;
+  }
+  trans_->write((const uint8_t *)val.c_str(), val.length());
+  result += val.length();
+  if (escapeNum) {
+    trans_->write(&kJSONStringDelimiter, 1);
+    result += 1;
+  }
+  return result;
+}
+
+uint32_t TJSONProtocol::writeJSONObjectStart() {
+  uint32_t result = context_->write(*trans_);
+  trans_->write(&kJSONObjectStart, 1);
+  pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
+  return result + 1;
+}
+
+uint32_t TJSONProtocol::writeJSONObjectEnd() {
+  popContext();
+  trans_->write(&kJSONObjectEnd, 1);
+  return 1;
+}
+
+uint32_t TJSONProtocol::writeJSONArrayStart() {
+  uint32_t result = context_->write(*trans_);
+  trans_->write(&kJSONArrayStart, 1);
+  pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
+  return result + 1;
+}
+
+uint32_t TJSONProtocol::writeJSONArrayEnd() {
+  popContext();
+  trans_->write(&kJSONArrayEnd, 1);
+  return 1;
+}
+
+uint32_t TJSONProtocol::writeMessageBegin(const std::string& name,
+                                          const TMessageType messageType,
+                                          const int32_t seqid) {
+  uint32_t result = writeJSONArrayStart();
+  result += writeJSONInteger(kThriftVersion1);
+  result += writeJSONString(name);
+  result += writeJSONInteger(messageType);
+  result += writeJSONInteger(seqid);
+  return result;
+}
+
+uint32_t TJSONProtocol::writeMessageEnd() {
+  return writeJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::writeStructBegin(const std::string& name) {
+  return writeJSONObjectStart();
+}
+
+uint32_t TJSONProtocol::writeStructEnd() {
+  return writeJSONObjectEnd();
+}
+
+uint32_t TJSONProtocol::writeFieldBegin(const std::string& name,
+                                        const TType fieldType,
+                                        const int16_t fieldId) {
+  uint32_t result = writeJSONInteger(fieldId);
+  result += writeJSONObjectStart();
+  result += writeJSONString(getTypeNameForTypeID(fieldType));
+  return result;
+}
+
+uint32_t TJSONProtocol::writeFieldEnd() {
+  return writeJSONObjectEnd();
+}
+
+uint32_t TJSONProtocol::writeFieldStop() {
+  return 0;
+}
+
+uint32_t TJSONProtocol::writeMapBegin(const TType keyType,
+                                      const TType valType,
+                                      const uint32_t size) {
+  uint32_t result = writeJSONArrayStart();
+  result += writeJSONString(getTypeNameForTypeID(keyType));
+  result += writeJSONString(getTypeNameForTypeID(valType));
+  result += writeJSONInteger((int64_t)size);
+  result += writeJSONObjectStart();
+  return result;
+}
+
+uint32_t TJSONProtocol::writeMapEnd() {
+  return writeJSONObjectEnd() + writeJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::writeListBegin(const TType elemType,
+                                       const uint32_t size) {
+  uint32_t result = writeJSONArrayStart();
+  result += writeJSONString(getTypeNameForTypeID(elemType));
+  result += writeJSONInteger((int64_t)size);
+  return result;
+}
+
+uint32_t TJSONProtocol::writeListEnd() {
+  return writeJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::writeSetBegin(const TType elemType,
+                                      const uint32_t size) {
+  uint32_t result = writeJSONArrayStart();
+  result += writeJSONString(getTypeNameForTypeID(elemType));
+  result += writeJSONInteger((int64_t)size);
+  return result;
+}
+
+uint32_t TJSONProtocol::writeSetEnd() {
+  return writeJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::writeBool(const bool value) {
+  return writeJSONInteger(value);
+}
+
+uint32_t TJSONProtocol::writeByte(const int8_t byte) {
+  // writeByte() must be handled properly becuase boost::lexical cast sees
+  // int8_t as a text type instead of an integer type
+  return writeJSONInteger((int16_t)byte);
+}
+
+uint32_t TJSONProtocol::writeI16(const int16_t i16) {
+  return writeJSONInteger(i16);
+}
+
+uint32_t TJSONProtocol::writeI32(const int32_t i32) {
+  return writeJSONInteger(i32);
+}
+
+uint32_t TJSONProtocol::writeI64(const int64_t i64) {
+  return writeJSONInteger(i64);
+}
+
+uint32_t TJSONProtocol::writeDouble(const double dub) {
+  return writeJSONDouble(dub);
+}
+
+uint32_t TJSONProtocol::writeString(const std::string& str) {
+  return writeJSONString(str);
+}
+
+uint32_t TJSONProtocol::writeBinary(const std::string& str) {
+  return writeJSONBase64(str);
+}
+
+  /**
+   * Reading functions
+   */
+
+// Reads 1 byte and verifires that it matches ch.
+uint32_t TJSONProtocol::readJSONSyntaxChar(uint8_t ch) {
+  return readSyntaxChar(*trans_, ch);
+}
+
+// Decodes the four hex parts of a JSON escaped string character and returns
+// the character via out. The first two characters must be "00".
+uint32_t TJSONProtocol::readJSONEscapeChar(uint8_t *out) {
+  uint8_t b[2];
+  readJSONSyntaxChar(kJSONZeroChar);
+  readJSONSyntaxChar(kJSONZeroChar);
+  trans_->readAll(b, 2);
+  *out = (hexVal(b[0]) << 4) + hexVal(b[1]);
+  return 4;
+}
+
+// Decodes a JSON string, including unescaping, and returns the string via str
+uint32_t TJSONProtocol::readJSONString(std::string &str, bool skipContext) {
+  uint32_t result = (skipContext ? 0 : context_->read(*trans_));
+  result += readJSONSyntaxChar(kJSONStringDelimiter);
+  uint8_t b[1];
+  while (true) {
+    result += trans_->readAll(b, 1);
+    if (b[0] == kJSONStringDelimiter) {
+      break;
+    }
+    if (b[0] == kJSONBackslash) {
+      result += trans_->readAll(b, 1);
+      if (b[0] == kJSONEscapeChar) {
+        result += readJSONEscapeChar(&b[0]);
+      }
+      else {
+        size_t pos = kEscapeChars.find(b[0]);
+        if (pos == std::string::npos) {
+          throw TProtocolException(TProtocolException::INVALID_DATA,
+                                   "Expected control char, got '" +
+                                     std::string((char *)b, 1) + "'.");
+        }
+        b[0] = kEscapeCharVals[pos];
+      }
+    }
+    str += b[0];
+  }
+  return result;
+}
+
+// Reads a block of base64 characters, decoding it, and returns via str
+uint32_t TJSONProtocol::readJSONBase64(std::string &str) {
+  std::string tmp;
+  uint32_t result = readJSONString(tmp);
+  uint8_t *b = (uint8_t *)tmp.c_str();
+  uint32_t len = tmp.length();
+  while (len >= 4) {
+    base64_decode(b, 4);
+    str.append((const char *)b, 3);
+    b += 4;
+    len -= 4;
+  }
+  // Don't decode if we hit the end or got a single leftover byte (invalid
+  // base64 but legal for skip of regular string type)
+  if (len > 1) {
+    base64_decode(b, len);
+    str.append((const char *)b, len - 1);
+  }
+  return result;
+}
+
+// Reads a sequence of characters, stopping at the first one that is not
+// a valid JSON numeric character.
+uint32_t TJSONProtocol::readJSONNumericChars(std::string &str) {
+  uint32_t result = 0;
+  while (true) {
+    uint8_t ch = borrowByte(*trans_);
+    if (!isJSONNumeric(ch)) {
+      break;
+    }
+    trans_->consume(1);
+    str += ch;
+    ++result;
+  }
+  return result;
+}
+
+// Reads a sequence of characters and assembles them into a number,
+// returning them via num
+template <typename NumberType>
+uint32_t TJSONProtocol::readJSONInteger(NumberType &num) {
+  uint32_t result = context_->read(*trans_);
+  if (context_->escapeNum()) {
+    result += readJSONSyntaxChar(kJSONStringDelimiter);
+  }
+  std::string str;
+  result += readJSONNumericChars(str);
+  try {
+    num = boost::lexical_cast<NumberType>(str);
+  }
+  catch (boost::bad_lexical_cast e) {
+    throw new TProtocolException(TProtocolException::INVALID_DATA,
+                                 "Expected numeric value; got \"" + str +
+                                  "\"");
+  }
+  if (context_->escapeNum()) {
+    result += readJSONSyntaxChar(kJSONStringDelimiter);
+  }
+  return result;
+}
+
+// Reads a JSON number or string and interprets it as a double.
+uint32_t TJSONProtocol::readJSONDouble(double &num) {
+  uint32_t result = context_->read(*trans_);
+  std::string str;
+  if (borrowByte(*trans_) == kJSONStringDelimiter) {
+    result += readJSONString(str, true);
+    // Check for NaN, Infinity and -Infinity
+    if (str == kThriftNan) {
+      num = HUGE_VAL/HUGE_VAL; // generates NaN
+    }
+    else if (str == kThriftInfinity) {
+      num = HUGE_VAL;
+    }
+    else if (str == kThriftNegativeInfinity) {
+      num = -HUGE_VAL;
+    }
+    else {
+      if (!context_->escapeNum()) {
+        // Throw exception -- we should not be in a string in this case
+        throw new TProtocolException(TProtocolException::INVALID_DATA,
+                                     "Numeric data unexpectedly quoted");
+      }
+      try {
+        num = boost::lexical_cast<double>(str);
+      }
+      catch (boost::bad_lexical_cast e) {
+        throw new TProtocolException(TProtocolException::INVALID_DATA,
+                                     "Expected numeric value; got \"" + str +
+                                     "\"");
+      }
+    }
+  }
+  else {
+    if (context_->escapeNum()) {
+      // This will throw - we should have had a quote if escapeNum == true
+      readJSONSyntaxChar(kJSONStringDelimiter);
+    }
+    result += readJSONNumericChars(str);
+    try {
+      num = boost::lexical_cast<double>(str);
+    }
+    catch (boost::bad_lexical_cast e) {
+      throw new TProtocolException(TProtocolException::INVALID_DATA,
+                                   "Expected numeric value; got \"" + str +
+                                   "\"");
+    }
+  }
+  return result;
+}
+
+uint32_t TJSONProtocol::readJSONObjectStart() {
+  uint32_t result = context_->read(*trans_);
+  result += readJSONSyntaxChar(kJSONObjectStart);
+  pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
+  return result;
+}
+
+uint32_t TJSONProtocol::readJSONObjectEnd() {
+  uint32_t result = readJSONSyntaxChar(kJSONObjectEnd);
+  popContext();
+  return result;
+}
+
+uint32_t TJSONProtocol::readJSONArrayStart() {
+  uint32_t result = context_->read(*trans_);
+  result += readJSONSyntaxChar(kJSONArrayStart);
+  pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
+  return result;
+}
+
+uint32_t TJSONProtocol::readJSONArrayEnd() {
+  uint32_t result = readJSONSyntaxChar(kJSONArrayEnd);
+  popContext();
+  return result;
+}
+
+uint32_t TJSONProtocol::readMessageBegin(std::string& name,
+                                         TMessageType& messageType,
+                                         int32_t& seqid) {
+  uint32_t result = readJSONArrayStart();
+  std::string tmpStr;
+  uint64_t tmpVal = 0;
+  result += readJSONInteger(tmpVal);
+  if (tmpVal != kThriftVersion1) {
+    throw TProtocolException(TProtocolException::BAD_VERSION,
+                             "Message contained bad version.");
+  }
+  result += readJSONString(name);
+  result += readJSONInteger(tmpVal);
+  messageType = (TMessageType)tmpVal;
+  result += readJSONInteger(tmpVal);
+  seqid = tmpVal;
+  return result;
+}
+
+uint32_t TJSONProtocol::readMessageEnd() {
+  return readJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::readStructBegin(std::string& name) {
+  return readJSONObjectStart();
+}
+
+uint32_t TJSONProtocol::readStructEnd() {
+  return readJSONObjectEnd();
+}
+
+uint32_t TJSONProtocol::readFieldBegin(std::string& name,
+                                       TType& fieldType,
+                                       int16_t& fieldId) {
+  // Check if we hit the end of the list
+  uint8_t b[1];
+  uint32_t len = 1;
+  const uint8_t * buf = trans_->borrow(b, &len);
+  if (!buf || !len) {
+    throw TTransportException(TTransportException::UNKNOWN,
+                              "Could not borrow 1 byte from transport.");
+  }
+  uint32_t result = 0;
+  if (buf[0] == kJSONObjectEnd) {
+    fieldType = facebook::thrift::protocol::T_STOP;
+  }
+  else {
+    uint64_t tmpVal = 0;
+    std::string tmpStr;
+    result += readJSONInteger(tmpVal);
+    fieldId = tmpVal;
+    result += readJSONObjectStart();
+    result += readJSONString(tmpStr);
+    fieldType = getTypeIDForTypeName(tmpStr);
+  }
+  return result;
+}
+
+uint32_t TJSONProtocol::readFieldEnd() {
+  return readJSONObjectEnd();
+}
+
+uint32_t TJSONProtocol::readMapBegin(TType& keyType,
+                                     TType& valType,
+                                     uint32_t& size) {
+  uint64_t tmpVal = 0;
+  std::string tmpStr;
+  uint32_t result = readJSONArrayStart();
+  result += readJSONString(tmpStr);
+  keyType = getTypeIDForTypeName(tmpStr);
+  result += readJSONString(tmpStr);
+  valType = getTypeIDForTypeName(tmpStr);
+  result += readJSONInteger(tmpVal);
+  size = tmpVal;
+  result += readJSONObjectStart();
+  return result;
+}
+
+uint32_t TJSONProtocol::readMapEnd() {
+  return readJSONObjectEnd() + readJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::readListBegin(TType& elemType,
+                                      uint32_t& size) {
+  uint64_t tmpVal = 0;
+  std::string tmpStr;
+  uint32_t result = readJSONArrayStart();
+  result += readJSONString(tmpStr);
+  elemType = getTypeIDForTypeName(tmpStr);
+  result += readJSONInteger(tmpVal);
+  size = tmpVal;
+  return result;
+}
+
+uint32_t TJSONProtocol::readListEnd() {
+  return readJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::readSetBegin(TType& elemType,
+                                     uint32_t& size) {
+  uint64_t tmpVal = 0;
+  std::string tmpStr;
+  uint32_t result = readJSONArrayStart();
+  result += readJSONString(tmpStr);
+  elemType = getTypeIDForTypeName(tmpStr);
+  result += readJSONInteger(tmpVal);
+  size = tmpVal;
+  return result;
+}
+
+uint32_t TJSONProtocol::readSetEnd() {
+  return readJSONArrayEnd();
+}
+
+uint32_t TJSONProtocol::readBool(bool& value) {
+  return readJSONInteger(value);
+}
+
+// readByte() must be handled properly becuase boost::lexical cast sees int8_t
+// as a text type instead of an integer type
+uint32_t TJSONProtocol::readByte(int8_t& byte) {
+  int16_t tmp = (int16_t) byte;
+  uint32_t result =  readJSONInteger(tmp);
+  assert(tmp < 256);
+  byte = (int8_t)tmp;
+  return result;
+}
+
+uint32_t TJSONProtocol::readI16(int16_t& i16) {
+  return readJSONInteger(i16);
+}
+
+uint32_t TJSONProtocol::readI32(int32_t& i32) {
+  return readJSONInteger(i32);
+}
+
+uint32_t TJSONProtocol::readI64(int64_t& i64) {
+  return readJSONInteger(i64);
+}
+
+uint32_t TJSONProtocol::readDouble(double& dub) {
+  return readJSONDouble(dub);
+}
+
+uint32_t TJSONProtocol::readString(std::string &str) {
+  return readJSONString(str);
+}
+
+uint32_t TJSONProtocol::readBinary(std::string &str) {
+  return readJSONBase64(str);
+}
+
+}}} // facebook::thrift::protocol