blob: 2be54da2d43bd835a5458768668e07a8f0bf400b [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
David Reissdb0ea152008-02-18 01:49:37 +000019
20#ifndef _THRIFT_PROTOCOL_TJSONPROTOCOL_H_
21#define _THRIFT_PROTOCOL_TJSONPROTOCOL_H_ 1
22
David Reiss6806fb82010-10-06 17:09:52 +000023#include "TVirtualProtocol.h"
David Reissdb0ea152008-02-18 01:49:37 +000024
25#include <stack>
26
T Jake Lucianib5e62212009-01-31 22:36:20 +000027namespace apache { namespace thrift { namespace protocol {
David Reissdb0ea152008-02-18 01:49:37 +000028
29// Forward declaration
30class TJSONContext;
31
32/**
33 * JSON protocol for Thrift.
34 *
David Reiss1e62ab42008-02-21 22:37:45 +000035 * Implements a protocol which uses JSON as the wire-format.
36 *
David Reissdb0ea152008-02-18 01:49:37 +000037 * Thrift types are represented as described below:
38 *
39 * 1. Every Thrift integer type is represented as a JSON number.
40 *
41 * 2. Thrift doubles are represented as JSON numbers. Some special values are
42 * represented as strings:
43 * a. "NaN" for not-a-number values
44 * b. "Infinity" for postive infinity
45 * c. "-Infinity" for negative infinity
46 *
47 * 3. Thrift string values are emitted as JSON strings, with appropriate
48 * escaping.
49 *
50 * 4. Thrift binary values are encoded into Base64 and emitted as JSON strings.
51 * The readBinary() method is written such that it will properly skip if
52 * called on a Thrift string (although it will decode garbage data).
53 *
54 * 5. Thrift structs are represented as JSON objects, with the field ID as the
55 * key, and the field value represented as a JSON object with a single
56 * key-value pair. The key is a short string identifier for that type,
57 * followed by the value. The valid type identifiers are: "tf" for bool,
58 * "i8" for byte, "i16" for 16-bit integer, "i32" for 32-bit integer, "i64"
59 * for 64-bit integer, "dbl" for double-precision loating point, "str" for
60 * string (including binary), "rec" for struct ("records"), "map" for map,
61 * "lst" for list, "set" for set.
62 *
63 * 6. Thrift lists and sets are represented as JSON arrays, with the first
64 * element of the JSON array being the string identifier for the Thrift
65 * element type and the second element of the JSON array being the count of
66 * the Thrift elements. The Thrift elements then follow.
67 *
68 * 7. Thrift maps are represented as JSON arrays, with the first two elements
69 * of the JSON array being the string identifiers for the Thrift key type
70 * and value type, followed by the count of the Thrift pairs, followed by a
71 * JSON object containing the key-value pairs. Note that JSON keys can only
72 * be strings, which means that the key type of the Thrift map should be
73 * restricted to numeric or string types -- in the case of numerics, they
74 * are serialized as strings.
75 *
76 * 8. Thrift messages are represented as JSON arrays, with the protocol
77 * version #, the message name, the message type, and the sequence ID as
78 * the first 4 elements.
79 *
80 * More discussion of the double handling is probably warranted. The aim of
81 * the current implementation is to match as closely as possible the behavior
82 * of Java's Double.toString(), which has no precision loss. Implementors in
83 * other languages should strive to achieve that where possible. I have not
84 * yet verified whether boost:lexical_cast, which is doing that work for me in
85 * C++, loses any precision, but I am leaving this as a future improvement. I
86 * may try to provide a C component for this, so that other languages could
87 * bind to the same underlying implementation for maximum consistency.
88 *
89 * Note further that JavaScript itself is not capable of representing
90 * floating point infinities -- presumably when we have a JavaScript Thrift
91 * client, this would mean that infinities get converted to not-a-number in
92 * transmission. I don't know of any work-around for this issue.
93 *
David Reissdb0ea152008-02-18 01:49:37 +000094 */
David Reiss6806fb82010-10-06 17:09:52 +000095class TJSONProtocol : public TVirtualProtocol<TJSONProtocol> {
David Reissdb0ea152008-02-18 01:49:37 +000096 public:
97
98 TJSONProtocol(boost::shared_ptr<TTransport> ptrans);
99
100 ~TJSONProtocol();
101
102 private:
103
104 void pushContext(boost::shared_ptr<TJSONContext> c);
105
106 void popContext();
107
108 uint32_t writeJSONEscapeChar(uint8_t ch);
109
110 uint32_t writeJSONChar(uint8_t ch);
111
112 uint32_t writeJSONString(const std::string &str);
113
114 uint32_t writeJSONBase64(const std::string &str);
115
116 template <typename NumberType>
117 uint32_t writeJSONInteger(NumberType num);
118
119 uint32_t writeJSONDouble(double num);
120
121 uint32_t writeJSONObjectStart() ;
122
123 uint32_t writeJSONObjectEnd();
124
125 uint32_t writeJSONArrayStart();
126
127 uint32_t writeJSONArrayEnd();
128
129 uint32_t readJSONSyntaxChar(uint8_t ch);
130
131 uint32_t readJSONEscapeChar(uint8_t *out);
132
133 uint32_t readJSONString(std::string &str, bool skipContext = false);
134
135 uint32_t readJSONBase64(std::string &str);
136
137 uint32_t readJSONNumericChars(std::string &str);
138
139 template <typename NumberType>
140 uint32_t readJSONInteger(NumberType &num);
141
142 uint32_t readJSONDouble(double &num);
143
144 uint32_t readJSONObjectStart();
145
146 uint32_t readJSONObjectEnd();
147
148 uint32_t readJSONArrayStart();
149
150 uint32_t readJSONArrayEnd();
151
152 public:
153
154 /**
155 * Writing functions.
156 */
157
158 uint32_t writeMessageBegin(const std::string& name,
159 const TMessageType messageType,
160 const int32_t seqid);
161
162 uint32_t writeMessageEnd();
163
David Reiss64120002008-04-29 23:12:24 +0000164 uint32_t writeStructBegin(const char* name);
David Reissdb0ea152008-02-18 01:49:37 +0000165
166 uint32_t writeStructEnd();
167
David Reiss64120002008-04-29 23:12:24 +0000168 uint32_t writeFieldBegin(const char* name,
David Reissdb0ea152008-02-18 01:49:37 +0000169 const TType fieldType,
170 const int16_t fieldId);
171
172 uint32_t writeFieldEnd();
173
174 uint32_t writeFieldStop();
175
176 uint32_t writeMapBegin(const TType keyType,
177 const TType valType,
178 const uint32_t size);
179
180 uint32_t writeMapEnd();
181
182 uint32_t writeListBegin(const TType elemType,
183 const uint32_t size);
184
185 uint32_t writeListEnd();
186
187 uint32_t writeSetBegin(const TType elemType,
188 const uint32_t size);
189
190 uint32_t writeSetEnd();
191
192 uint32_t writeBool(const bool value);
193
194 uint32_t writeByte(const int8_t byte);
195
196 uint32_t writeI16(const int16_t i16);
197
198 uint32_t writeI32(const int32_t i32);
199
200 uint32_t writeI64(const int64_t i64);
201
202 uint32_t writeDouble(const double dub);
203
204 uint32_t writeString(const std::string& str);
205
206 uint32_t writeBinary(const std::string& str);
207
208 /**
209 * Reading functions
210 */
211
212 uint32_t readMessageBegin(std::string& name,
213 TMessageType& messageType,
214 int32_t& seqid);
215
216 uint32_t readMessageEnd();
217
218 uint32_t readStructBegin(std::string& name);
219
220 uint32_t readStructEnd();
221
222 uint32_t readFieldBegin(std::string& name,
223 TType& fieldType,
224 int16_t& fieldId);
225
226 uint32_t readFieldEnd();
227
228 uint32_t readMapBegin(TType& keyType,
229 TType& valType,
230 uint32_t& size);
231
232 uint32_t readMapEnd();
233
234 uint32_t readListBegin(TType& elemType,
235 uint32_t& size);
236
237 uint32_t readListEnd();
238
239 uint32_t readSetBegin(TType& elemType,
240 uint32_t& size);
241
242 uint32_t readSetEnd();
243
244 uint32_t readBool(bool& value);
245
David Reiss8dfc7322010-10-06 17:09:58 +0000246 // Provide the default readBool() implementation for std::vector<bool>
247 using TVirtualProtocol<TJSONProtocol>::readBool;
248
David Reissdb0ea152008-02-18 01:49:37 +0000249 uint32_t readByte(int8_t& byte);
250
251 uint32_t readI16(int16_t& i16);
252
253 uint32_t readI32(int32_t& i32);
254
255 uint32_t readI64(int64_t& i64);
256
257 uint32_t readDouble(double& dub);
258
259 uint32_t readString(std::string& str);
260
261 uint32_t readBinary(std::string& str);
262
David Reiss1e62ab42008-02-21 22:37:45 +0000263 class LookaheadReader {
264
265 public:
266
267 LookaheadReader(TTransport &trans) :
268 trans_(&trans),
269 hasData_(false) {
270 }
271
272 uint8_t read() {
273 if (hasData_) {
274 hasData_ = false;
275 }
276 else {
277 trans_->readAll(&data_, 1);
278 }
279 return data_;
280 }
281
282 uint8_t peek() {
283 if (!hasData_) {
284 trans_->readAll(&data_, 1);
285 }
286 hasData_ = true;
287 return data_;
288 }
289
290 private:
291 TTransport *trans_;
292 bool hasData_;
293 uint8_t data_;
294 };
295
David Reissdb0ea152008-02-18 01:49:37 +0000296 private:
David Reisse71115b2010-10-06 17:09:56 +0000297 TTransport* trans_;
David Reissdb0ea152008-02-18 01:49:37 +0000298
299 std::stack<boost::shared_ptr<TJSONContext> > contexts_;
300 boost::shared_ptr<TJSONContext> context_;
David Reiss1e62ab42008-02-21 22:37:45 +0000301 LookaheadReader reader_;
David Reissdb0ea152008-02-18 01:49:37 +0000302};
303
304/**
305 * Constructs input and output protocol objects given transports.
306 */
David Reiss4c266cc2009-01-15 23:56:24 +0000307class TJSONProtocolFactory : public TProtocolFactory {
David Reissdb0ea152008-02-18 01:49:37 +0000308 public:
309 TJSONProtocolFactory() {}
310
311 virtual ~TJSONProtocolFactory() {}
312
313 boost::shared_ptr<TProtocol> getProtocol(boost::shared_ptr<TTransport> trans) {
314 return boost::shared_ptr<TProtocol>(new TJSONProtocol(trans));
315 }
316};
317
T Jake Lucianib5e62212009-01-31 22:36:20 +0000318}}} // apache::thrift::protocol
David Reissdb0ea152008-02-18 01:49:37 +0000319
320
David Reiss28f298d2008-05-01 06:17:36 +0000321// TODO(dreiss): Move part of ThriftJSONString into a .cpp file and remove this.
322#include <transport/TBufferTransports.h>
323
T Jake Lucianib5e62212009-01-31 22:36:20 +0000324namespace apache { namespace thrift {
David Reissdb0ea152008-02-18 01:49:37 +0000325
326template<typename ThriftStruct>
327 std::string ThriftJSONString(const ThriftStruct& ts) {
T Jake Lucianib5e62212009-01-31 22:36:20 +0000328 using namespace apache::thrift::transport;
329 using namespace apache::thrift::protocol;
David Reissdb0ea152008-02-18 01:49:37 +0000330 TMemoryBuffer* buffer = new TMemoryBuffer;
331 boost::shared_ptr<TTransport> trans(buffer);
332 TJSONProtocol protocol(trans);
333
334 ts.write(&protocol);
335
336 uint8_t* buf;
337 uint32_t size;
338 buffer->getBuffer(&buf, &size);
339 return std::string((char*)buf, (unsigned int)size);
340}
341
T Jake Lucianib5e62212009-01-31 22:36:20 +0000342}} // apache::thrift
David Reissdb0ea152008-02-18 01:49:37 +0000343
344#endif // #define _THRIFT_PROTOCOL_TJSONPROTOCOL_H_ 1