blob: efb8e636a2ae410b2b8eba4b0f27a1c46e5d6343 [file] [log] [blame]
David Reissdb0ea152008-02-18 01:49:37 +00001// Copyright (c) 2006- Facebook
2// Distributed under the Thrift Software License
3//
4// See accompanying file LICENSE or visit the Thrift site at:
5// http://developers.facebook.com/thrift/
6
7#ifndef _THRIFT_PROTOCOL_TJSONPROTOCOL_H_
8#define _THRIFT_PROTOCOL_TJSONPROTOCOL_H_ 1
9
10#include "TProtocol.h"
11#include <transport/TTransportUtils.h>
12
13#include <stack>
14
15namespace facebook { namespace thrift { namespace protocol {
16
17// Forward declaration
18class TJSONContext;
19
20/**
21 * JSON protocol for Thrift.
22 *
David Reiss1e62ab42008-02-21 22:37:45 +000023 * Implements a protocol which uses JSON as the wire-format.
24 *
David Reissdb0ea152008-02-18 01:49:37 +000025 * Thrift types are represented as described below:
26 *
27 * 1. Every Thrift integer type is represented as a JSON number.
28 *
29 * 2. Thrift doubles are represented as JSON numbers. Some special values are
30 * represented as strings:
31 * a. "NaN" for not-a-number values
32 * b. "Infinity" for postive infinity
33 * c. "-Infinity" for negative infinity
34 *
35 * 3. Thrift string values are emitted as JSON strings, with appropriate
36 * escaping.
37 *
38 * 4. Thrift binary values are encoded into Base64 and emitted as JSON strings.
39 * The readBinary() method is written such that it will properly skip if
40 * called on a Thrift string (although it will decode garbage data).
41 *
42 * 5. Thrift structs are represented as JSON objects, with the field ID as the
43 * key, and the field value represented as a JSON object with a single
44 * key-value pair. The key is a short string identifier for that type,
45 * followed by the value. The valid type identifiers are: "tf" for bool,
46 * "i8" for byte, "i16" for 16-bit integer, "i32" for 32-bit integer, "i64"
47 * for 64-bit integer, "dbl" for double-precision loating point, "str" for
48 * string (including binary), "rec" for struct ("records"), "map" for map,
49 * "lst" for list, "set" for set.
50 *
51 * 6. Thrift lists and sets are represented as JSON arrays, with the first
52 * element of the JSON array being the string identifier for the Thrift
53 * element type and the second element of the JSON array being the count of
54 * the Thrift elements. The Thrift elements then follow.
55 *
56 * 7. Thrift maps are represented as JSON arrays, with the first two elements
57 * of the JSON array being the string identifiers for the Thrift key type
58 * and value type, followed by the count of the Thrift pairs, followed by a
59 * JSON object containing the key-value pairs. Note that JSON keys can only
60 * be strings, which means that the key type of the Thrift map should be
61 * restricted to numeric or string types -- in the case of numerics, they
62 * are serialized as strings.
63 *
64 * 8. Thrift messages are represented as JSON arrays, with the protocol
65 * version #, the message name, the message type, and the sequence ID as
66 * the first 4 elements.
67 *
68 * More discussion of the double handling is probably warranted. The aim of
69 * the current implementation is to match as closely as possible the behavior
70 * of Java's Double.toString(), which has no precision loss. Implementors in
71 * other languages should strive to achieve that where possible. I have not
72 * yet verified whether boost:lexical_cast, which is doing that work for me in
73 * C++, loses any precision, but I am leaving this as a future improvement. I
74 * may try to provide a C component for this, so that other languages could
75 * bind to the same underlying implementation for maximum consistency.
76 *
77 * Note further that JavaScript itself is not capable of representing
78 * floating point infinities -- presumably when we have a JavaScript Thrift
79 * client, this would mean that infinities get converted to not-a-number in
80 * transmission. I don't know of any work-around for this issue.
81 *
82 * @author Chad Walters <chad@powerset.com>
83 */
84class TJSONProtocol : public TProtocol {
85 public:
86
87 TJSONProtocol(boost::shared_ptr<TTransport> ptrans);
88
89 ~TJSONProtocol();
90
91 private:
92
93 void pushContext(boost::shared_ptr<TJSONContext> c);
94
95 void popContext();
96
97 uint32_t writeJSONEscapeChar(uint8_t ch);
98
99 uint32_t writeJSONChar(uint8_t ch);
100
101 uint32_t writeJSONString(const std::string &str);
102
103 uint32_t writeJSONBase64(const std::string &str);
104
105 template <typename NumberType>
106 uint32_t writeJSONInteger(NumberType num);
107
108 uint32_t writeJSONDouble(double num);
109
110 uint32_t writeJSONObjectStart() ;
111
112 uint32_t writeJSONObjectEnd();
113
114 uint32_t writeJSONArrayStart();
115
116 uint32_t writeJSONArrayEnd();
117
118 uint32_t readJSONSyntaxChar(uint8_t ch);
119
120 uint32_t readJSONEscapeChar(uint8_t *out);
121
122 uint32_t readJSONString(std::string &str, bool skipContext = false);
123
124 uint32_t readJSONBase64(std::string &str);
125
126 uint32_t readJSONNumericChars(std::string &str);
127
128 template <typename NumberType>
129 uint32_t readJSONInteger(NumberType &num);
130
131 uint32_t readJSONDouble(double &num);
132
133 uint32_t readJSONObjectStart();
134
135 uint32_t readJSONObjectEnd();
136
137 uint32_t readJSONArrayStart();
138
139 uint32_t readJSONArrayEnd();
140
141 public:
142
143 /**
144 * Writing functions.
145 */
146
147 uint32_t writeMessageBegin(const std::string& name,
148 const TMessageType messageType,
149 const int32_t seqid);
150
151 uint32_t writeMessageEnd();
152
David Reiss64120002008-04-29 23:12:24 +0000153 uint32_t writeStructBegin(const char* name);
David Reissdb0ea152008-02-18 01:49:37 +0000154
155 uint32_t writeStructEnd();
156
David Reiss64120002008-04-29 23:12:24 +0000157 uint32_t writeFieldBegin(const char* name,
David Reissdb0ea152008-02-18 01:49:37 +0000158 const TType fieldType,
159 const int16_t fieldId);
160
161 uint32_t writeFieldEnd();
162
163 uint32_t writeFieldStop();
164
165 uint32_t writeMapBegin(const TType keyType,
166 const TType valType,
167 const uint32_t size);
168
169 uint32_t writeMapEnd();
170
171 uint32_t writeListBegin(const TType elemType,
172 const uint32_t size);
173
174 uint32_t writeListEnd();
175
176 uint32_t writeSetBegin(const TType elemType,
177 const uint32_t size);
178
179 uint32_t writeSetEnd();
180
181 uint32_t writeBool(const bool value);
182
183 uint32_t writeByte(const int8_t byte);
184
185 uint32_t writeI16(const int16_t i16);
186
187 uint32_t writeI32(const int32_t i32);
188
189 uint32_t writeI64(const int64_t i64);
190
191 uint32_t writeDouble(const double dub);
192
193 uint32_t writeString(const std::string& str);
194
195 uint32_t writeBinary(const std::string& str);
196
197 /**
198 * Reading functions
199 */
200
201 uint32_t readMessageBegin(std::string& name,
202 TMessageType& messageType,
203 int32_t& seqid);
204
205 uint32_t readMessageEnd();
206
207 uint32_t readStructBegin(std::string& name);
208
209 uint32_t readStructEnd();
210
211 uint32_t readFieldBegin(std::string& name,
212 TType& fieldType,
213 int16_t& fieldId);
214
215 uint32_t readFieldEnd();
216
217 uint32_t readMapBegin(TType& keyType,
218 TType& valType,
219 uint32_t& size);
220
221 uint32_t readMapEnd();
222
223 uint32_t readListBegin(TType& elemType,
224 uint32_t& size);
225
226 uint32_t readListEnd();
227
228 uint32_t readSetBegin(TType& elemType,
229 uint32_t& size);
230
231 uint32_t readSetEnd();
232
233 uint32_t readBool(bool& value);
234
235 uint32_t readByte(int8_t& byte);
236
237 uint32_t readI16(int16_t& i16);
238
239 uint32_t readI32(int32_t& i32);
240
241 uint32_t readI64(int64_t& i64);
242
243 uint32_t readDouble(double& dub);
244
245 uint32_t readString(std::string& str);
246
247 uint32_t readBinary(std::string& str);
248
David Reiss1e62ab42008-02-21 22:37:45 +0000249 class LookaheadReader {
250
251 public:
252
253 LookaheadReader(TTransport &trans) :
254 trans_(&trans),
255 hasData_(false) {
256 }
257
258 uint8_t read() {
259 if (hasData_) {
260 hasData_ = false;
261 }
262 else {
263 trans_->readAll(&data_, 1);
264 }
265 return data_;
266 }
267
268 uint8_t peek() {
269 if (!hasData_) {
270 trans_->readAll(&data_, 1);
271 }
272 hasData_ = true;
273 return data_;
274 }
275
276 private:
277 TTransport *trans_;
278 bool hasData_;
279 uint8_t data_;
280 };
281
David Reissdb0ea152008-02-18 01:49:37 +0000282 private:
283
284 std::stack<boost::shared_ptr<TJSONContext> > contexts_;
285 boost::shared_ptr<TJSONContext> context_;
David Reiss1e62ab42008-02-21 22:37:45 +0000286 LookaheadReader reader_;
David Reissdb0ea152008-02-18 01:49:37 +0000287};
288
289/**
290 * Constructs input and output protocol objects given transports.
291 */
292class TJSONProtocolFactory {
293 public:
294 TJSONProtocolFactory() {}
295
296 virtual ~TJSONProtocolFactory() {}
297
298 boost::shared_ptr<TProtocol> getProtocol(boost::shared_ptr<TTransport> trans) {
299 return boost::shared_ptr<TProtocol>(new TJSONProtocol(trans));
300 }
301};
302
303}}} // facebook::thrift::protocol
304
305
306namespace facebook { namespace thrift {
307
308template<typename ThriftStruct>
309 std::string ThriftJSONString(const ThriftStruct& ts) {
310 using namespace facebook::thrift::transport;
311 using namespace facebook::thrift::protocol;
312 TMemoryBuffer* buffer = new TMemoryBuffer;
313 boost::shared_ptr<TTransport> trans(buffer);
314 TJSONProtocol protocol(trans);
315
316 ts.write(&protocol);
317
318 uint8_t* buf;
319 uint32_t size;
320 buffer->getBuffer(&buf, &size);
321 return std::string((char*)buf, (unsigned int)size);
322}
323
324}} // facebook::thrift
325
326#endif // #define _THRIFT_PROTOCOL_TJSONPROTOCOL_H_ 1