├── LICENSE ├── Makefile ├── bin2ascii.h ├── json2pb.cc ├── json2pb.h ├── json2pb.py ├── test.json ├── test.proto └── test_json.cc /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Pavel Shramov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CPPFLAGS = -g -fPIC -I. 2 | LDFLAGS = -Wl,-rpath -Wl,. 3 | 4 | all: libjson2pb.so test_json 5 | 6 | clean: 7 | -rm -f *.o *.so *.a libjson2pb.so.* test 8 | 9 | test_json: test_json.o test.pb.o libjson2pb.so -lprotobuf 10 | test_json.o: test.pb.h 11 | 12 | json2pb.o: bin2ascii.h 13 | 14 | libjson2pb.so: json2pb.o 15 | $(CC) $(LDFLAGS) -o $@ $^ -Wl,-soname=$@ -Wl,-h -Wl,$@ -shared -L. -lcurl -lprotobuf -lstdc++ -ljansson 16 | 17 | test.pb.h test.pb.cc: test.proto 18 | protoc --cpp_out=$(shell pwd) test.proto 19 | -------------------------------------------------------------------------------- /bin2ascii.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Pavel Shramov 3 | * 4 | * json2pb is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #ifndef __BIN2ASCII_H__ 9 | #define __BIN2ASCII_H__ 10 | 11 | #include 12 | #include 13 | 14 | inline std::string hex2bin(const std::string &s) 15 | { 16 | if (s.size() % 2) 17 | throw std::runtime_error("Odd hex data size"); 18 | static const char lookup[] = "" 19 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x00 20 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x10 21 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x20 22 | "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x80\x80\x80\x80\x80\x80" // 0x30 23 | "\x80\x0a\x0b\x0c\x0d\x0e\x0f\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x40 24 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x50 25 | "\x80\x0a\x0b\x0c\x0d\x0e\x0f\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x60 26 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x70 27 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x80 28 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x90 29 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xa0 30 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xb0 31 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xc0 32 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xd0 33 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xe0 34 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xf0 35 | ""; 36 | std::string r; 37 | r.reserve(s.size() / 2); 38 | for (size_t i = 0; i < s.size(); i += 2) { 39 | char hi = lookup[s[i]]; 40 | char lo = lookup[s[i+1]]; 41 | if (0x80 & (hi | lo)) 42 | throw std::runtime_error("Invalid hex data: " + s.substr(i, 6)); 43 | r.push_back((hi << 4) | lo); 44 | } 45 | return r; 46 | } 47 | 48 | inline std::string bin2hex(const std::string &s) 49 | { 50 | static const char lookup[] = "0123456789abcdef"; 51 | std::string r; 52 | r.reserve(s.size() * 2); 53 | for (size_t i = 0; i < s.size(); i++) { 54 | char hi = s[i] >> 4; 55 | char lo = s[i] & 0xf; 56 | r.push_back(lookup[hi]); 57 | r.push_back(lookup[lo]); 58 | } 59 | return r; 60 | } 61 | 62 | inline std::string b64_encode(const std::string &s) 63 | { 64 | typedef unsigned char u1; 65 | static const char lookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 66 | const u1 * data = (const u1 *) s.c_str(); 67 | std::string r; 68 | r.reserve(s.size() * 4 / 3 + 3); 69 | for (size_t i = 0; i < s.size(); i += 3) { 70 | unsigned n = data[i] << 16; 71 | if (i + 1 < s.size()) n |= data[i + 1] << 8; 72 | if (i + 2 < s.size()) n |= data[i + 2]; 73 | 74 | u1 n0 = (u1)(n >> 18) & 0x3f; 75 | u1 n1 = (u1)(n >> 12) & 0x3f; 76 | u1 n2 = (u1)(n >> 6) & 0x3f; 77 | u1 n3 = (u1)(n ) & 0x3f; 78 | 79 | r.push_back(lookup[n0]); 80 | r.push_back(lookup[n1]); 81 | if (i + 1 < s.size()) r.push_back(lookup[n2]); 82 | if (i + 2 < s.size()) r.push_back(lookup[n3]); 83 | } 84 | for (int i = 0; i < (3 - s.size() % 3) % 3; i++) 85 | r.push_back('='); 86 | return r; 87 | } 88 | 89 | inline std::string b64_decode(const std::string &s) 90 | { 91 | typedef unsigned char u1; 92 | static const char lookup[] = "" 93 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x00 94 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x10 95 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x3e\x80\x80\x80\x3f" // 0x20 96 | "\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x80\x80\x80\x00\x80\x80" // 0x30 97 | "\x80\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e" // 0x40 98 | "\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x80\x80\x80\x80\x80" // 0x50 99 | "\x80\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28" // 0x60 100 | "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x80\x80\x80\x80\x80" // 0x70 101 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x80 102 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0x90 103 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xa0 104 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xb0 105 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xc0 106 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xd0 107 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xe0 108 | "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" // 0xf0 109 | ""; 110 | std::string r; 111 | if (!s.size()) return r; 112 | if (s.size() % 4) 113 | throw std::runtime_error("Invalid base64 data size"); 114 | size_t pad = 0; 115 | if (s[s.size() - 1] == '=') pad++; 116 | if (s[s.size() - 2] == '=') pad++; 117 | 118 | r.reserve(s.size() * 3 / 4 + 3); 119 | for (size_t i = 0; i < s.size(); i += 4) { 120 | u1 n0 = lookup[(u1) s[i+0]]; 121 | u1 n1 = lookup[(u1) s[i+1]]; 122 | u1 n2 = lookup[(u1) s[i+2]]; 123 | u1 n3 = lookup[(u1) s[i+3]]; 124 | if (0x80 & (n0 | n1 | n2 | n3)) 125 | throw std::runtime_error("Invalid hex data: " + s.substr(i, 4)); 126 | unsigned n = (n0 << 18) | (n1 << 12) | (n2 << 6) | n3; 127 | r.push_back((n >> 16) & 0xff); 128 | if (s[i+2] != '=') r.push_back((n >> 8) & 0xff); 129 | if (s[i+3] != '=') r.push_back((n ) & 0xff); 130 | } 131 | return r; 132 | } 133 | 134 | #endif//__BIN2ASCII_H__ 135 | -------------------------------------------------------------------------------- /json2pb.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Pavel Shramov 3 | * 4 | * json2pb is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | 18 | namespace { 19 | #include "bin2ascii.h" 20 | } 21 | 22 | using google::protobuf::Message; 23 | using google::protobuf::MessageFactory; 24 | using google::protobuf::Descriptor; 25 | using google::protobuf::FieldDescriptor; 26 | using google::protobuf::EnumDescriptor; 27 | using google::protobuf::EnumValueDescriptor; 28 | using google::protobuf::Reflection; 29 | 30 | struct json_autoptr { 31 | json_t * ptr; 32 | json_autoptr(json_t *json) : ptr(json) {} 33 | ~json_autoptr() { if (ptr) json_decref(ptr); } 34 | json_t * release() { json_t *tmp = ptr; ptr = 0; return tmp; } 35 | }; 36 | 37 | class j2pb_error : public std::exception { 38 | std::string _error; 39 | public: 40 | j2pb_error(const std::string &e) : _error(e) {} 41 | j2pb_error(const FieldDescriptor *field, const std::string &e) : _error(field->name() + ": " + e) {} 42 | virtual ~j2pb_error() throw() {}; 43 | 44 | virtual const char *what() const throw () { return _error.c_str(); }; 45 | }; 46 | 47 | static json_t * _pb2json(const Message& msg); 48 | static json_t * _field2json(const Message& msg, const FieldDescriptor *field, size_t index) 49 | { 50 | const Reflection *ref = msg.GetReflection(); 51 | const bool repeated = field->is_repeated(); 52 | json_t *jf = 0; 53 | switch (field->cpp_type()) 54 | { 55 | #define _CONVERT(type, ctype, fmt, sfunc, afunc) \ 56 | case FieldDescriptor::type: { \ 57 | const ctype value = (repeated)? \ 58 | ref->afunc(msg, field, index): \ 59 | ref->sfunc(msg, field); \ 60 | jf = fmt(value); \ 61 | break; \ 62 | } 63 | 64 | _CONVERT(CPPTYPE_DOUBLE, double, json_real, GetDouble, GetRepeatedDouble); 65 | _CONVERT(CPPTYPE_FLOAT, double, json_real, GetFloat, GetRepeatedFloat); 66 | _CONVERT(CPPTYPE_INT64, json_int_t, json_integer, GetInt64, GetRepeatedInt64); 67 | _CONVERT(CPPTYPE_UINT64, json_int_t, json_integer, GetUInt64, GetRepeatedUInt64); 68 | _CONVERT(CPPTYPE_INT32, json_int_t, json_integer, GetInt32, GetRepeatedInt32); 69 | _CONVERT(CPPTYPE_UINT32, json_int_t, json_integer, GetUInt32, GetRepeatedUInt32); 70 | _CONVERT(CPPTYPE_BOOL, bool, json_boolean, GetBool, GetRepeatedBool); 71 | #undef _CONVERT 72 | case FieldDescriptor::CPPTYPE_STRING: { 73 | std::string scratch; 74 | const std::string &value = (repeated)? 75 | ref->GetRepeatedStringReference(msg, field, index, &scratch): 76 | ref->GetStringReference(msg, field, &scratch); 77 | if (field->type() == FieldDescriptor::TYPE_BYTES) 78 | jf = json_string(b64_encode(value).c_str()); 79 | else 80 | jf = json_string(value.c_str()); 81 | break; 82 | } 83 | case FieldDescriptor::CPPTYPE_MESSAGE: { 84 | const Message& mf = (repeated)? 85 | ref->GetRepeatedMessage(msg, field, index): 86 | ref->GetMessage(msg, field); 87 | jf = _pb2json(mf); 88 | break; 89 | } 90 | case FieldDescriptor::CPPTYPE_ENUM: { 91 | const EnumValueDescriptor* ef = (repeated)? 92 | ref->GetRepeatedEnum(msg, field, index): 93 | ref->GetEnum(msg, field); 94 | 95 | jf = json_integer(ef->number()); 96 | break; 97 | } 98 | default: 99 | break; 100 | } 101 | if (!jf) throw j2pb_error(field, "Fail to convert to json"); 102 | return jf; 103 | } 104 | 105 | static json_t * _pb2json(const Message& msg) 106 | { 107 | const Descriptor *d = msg.GetDescriptor(); 108 | const Reflection *ref = msg.GetReflection(); 109 | if (!d || !ref) return 0; 110 | 111 | json_t *root = json_object(); 112 | json_autoptr _auto(root); 113 | 114 | std::vector fields; 115 | ref->ListFields(msg, &fields); 116 | 117 | for (size_t i = 0; i != fields.size(); i++) 118 | { 119 | const FieldDescriptor *field = fields[i]; 120 | 121 | json_t *jf = 0; 122 | if(field->is_repeated()) { 123 | size_t count = ref->FieldSize(msg, field); 124 | if (!count) continue; 125 | 126 | json_autoptr array(json_array()); 127 | for (size_t j = 0; j < count; j++) 128 | json_array_append_new(array.ptr, _field2json(msg, field, j)); 129 | jf = array.release(); 130 | } else if (ref->HasField(msg, field)) 131 | jf = _field2json(msg, field, 0); 132 | else 133 | continue; 134 | 135 | const std::string &name = (field->is_extension())?field->full_name():field->name(); 136 | json_object_set_new(root, name.c_str(), jf); 137 | } 138 | return _auto.release(); 139 | } 140 | 141 | static void _json2pb(Message& msg, json_t *root); 142 | static void _json2field(Message &msg, const FieldDescriptor *field, json_t *jf) 143 | { 144 | const Reflection *ref = msg.GetReflection(); 145 | const bool repeated = field->is_repeated(); 146 | json_error_t error; 147 | 148 | switch (field->cpp_type()) 149 | { 150 | #define _SET_OR_ADD(sfunc, afunc, value) \ 151 | do { \ 152 | if (repeated) \ 153 | ref->afunc(&msg, field, value); \ 154 | else \ 155 | ref->sfunc(&msg, field, value); \ 156 | } while (0) 157 | 158 | #define _CONVERT(type, ctype, fmt, sfunc, afunc) \ 159 | case FieldDescriptor::type: { \ 160 | ctype value; \ 161 | int r = json_unpack_ex(jf, &error, JSON_STRICT, fmt, &value); \ 162 | if (r) throw j2pb_error(field, std::string("Failed to unpack: ") + error.text); \ 163 | _SET_OR_ADD(sfunc, afunc, value); \ 164 | break; \ 165 | } 166 | 167 | _CONVERT(CPPTYPE_DOUBLE, double, "F", SetDouble, AddDouble); 168 | _CONVERT(CPPTYPE_FLOAT, double, "F", SetFloat, AddFloat); 169 | _CONVERT(CPPTYPE_INT64, json_int_t, "I", SetInt64, AddInt64); 170 | _CONVERT(CPPTYPE_UINT64, json_int_t, "I", SetUInt64, AddUInt64); 171 | _CONVERT(CPPTYPE_INT32, json_int_t, "I", SetInt32, AddInt32); 172 | _CONVERT(CPPTYPE_UINT32, json_int_t, "I", SetUInt32, AddUInt32); 173 | _CONVERT(CPPTYPE_BOOL, int, "b", SetBool, AddBool); 174 | 175 | case FieldDescriptor::CPPTYPE_STRING: { 176 | if (!json_is_string(jf)) 177 | throw j2pb_error(field, "Not a string"); 178 | const char * value = json_string_value(jf); 179 | if(field->type() == FieldDescriptor::TYPE_BYTES) 180 | _SET_OR_ADD(SetString, AddString, b64_decode(value)); 181 | else 182 | _SET_OR_ADD(SetString, AddString, value); 183 | break; 184 | } 185 | case FieldDescriptor::CPPTYPE_MESSAGE: { 186 | Message *mf = (repeated)? 187 | ref->AddMessage(&msg, field): 188 | ref->MutableMessage(&msg, field); 189 | _json2pb(*mf, jf); 190 | break; 191 | } 192 | case FieldDescriptor::CPPTYPE_ENUM: { 193 | const EnumDescriptor *ed = field->enum_type(); 194 | const EnumValueDescriptor *ev = 0; 195 | if (json_is_integer(jf)) { 196 | ev = ed->FindValueByNumber(json_integer_value(jf)); 197 | } else if (json_is_string(jf)) { 198 | ev = ed->FindValueByName(json_string_value(jf)); 199 | } else 200 | throw j2pb_error(field, "Not an integer or string"); 201 | if (!ev) 202 | throw j2pb_error(field, "Enum value not found"); 203 | _SET_OR_ADD(SetEnum, AddEnum, ev); 204 | break; 205 | } 206 | default: 207 | break; 208 | } 209 | } 210 | 211 | static void _json2pb(Message& msg, json_t *root) 212 | { 213 | const Descriptor *d = msg.GetDescriptor(); 214 | const Reflection *ref = msg.GetReflection(); 215 | if (!d || !ref) throw j2pb_error("No descriptor or reflection"); 216 | 217 | for (void *i = json_object_iter(root); i; i = json_object_iter_next(root, i)) 218 | { 219 | const char *name = json_object_iter_key(i); 220 | json_t *jf = json_object_iter_value(i); 221 | 222 | const FieldDescriptor *field = d->FindFieldByName(name); 223 | if (!field) 224 | field = ref->FindKnownExtensionByName(name); 225 | //field = d->file()->FindExtensionByName(name); 226 | 227 | if (!field) throw j2pb_error("Unknown field: " + std::string(name)); 228 | 229 | int r = 0; 230 | if (field->is_repeated()) { 231 | if (!json_is_array(jf)) 232 | throw j2pb_error(field, "Not array"); 233 | for (size_t j = 0; j < json_array_size(jf); j++) 234 | _json2field(msg, field, json_array_get(jf, j)); 235 | } else 236 | _json2field(msg, field, jf); 237 | } 238 | } 239 | 240 | void json2pb(Message &msg, const char *buf, size_t size) 241 | { 242 | json_t *root; 243 | json_error_t error; 244 | 245 | root = json_loadb(buf, size, 0, &error); 246 | 247 | if (!root) 248 | throw j2pb_error(std::string("Load failed: ") + error.text); 249 | 250 | json_autoptr _auto(root); 251 | 252 | if (!json_is_object(root)) 253 | throw j2pb_error("Malformed JSON: not an object"); 254 | 255 | _json2pb(msg, root); 256 | } 257 | 258 | int json_dump_std_string(const char *buf, size_t size, void *data) 259 | { 260 | std::string *s = (std::string *) data; 261 | s->append(buf, size); 262 | return 0; 263 | } 264 | 265 | std::string pb2json(const Message &msg) 266 | { 267 | std::string r; 268 | 269 | json_t *root = _pb2json(msg); 270 | json_autoptr _auto(root); 271 | json_dump_callback(root, json_dump_std_string, &r, 0); 272 | return r; 273 | } 274 | -------------------------------------------------------------------------------- /json2pb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Pavel Shramov 3 | * 4 | * json2pb is free software; you can redistribute it and/or modify 5 | * it under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #ifndef __JSON2PB_H__ 9 | #define __JSON2PB_H__ 10 | 11 | #include 12 | 13 | namespace google { 14 | namespace protobuf { 15 | class Message; 16 | } 17 | } 18 | 19 | void json2pb(google::protobuf::Message &msg, const char *buf, size_t size); 20 | std::string pb2json(const google::protobuf::Message &msg); 21 | 22 | #endif//__JSON2PB_H__ 23 | -------------------------------------------------------------------------------- /json2pb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: sts=4 sw=4 et 3 | 4 | """ 5 | Copyright (c) 2008-2013 Pavel Shramov 6 | 7 | json2pb is free software; you can redistribute it and/or modify 8 | it under the terms of the MIT license. See LICENSE for details. 9 | 10 | json2pb originaly was part of pbufrpc project 11 | """ 12 | 13 | """ 14 | Patch Message class with two functions: 15 | SerializeToJSON which works like SerializeToString but results in JSON message 16 | ParseFromJSON which parses JSON message and fills object 17 | Import this file to add functions 18 | Also they may be used as json_encode and json_decode 19 | """ 20 | 21 | from google.protobuf.message import Message 22 | 23 | 24 | __all__ = ['json_encode', 'json_decode'] 25 | 26 | Message.SerializeToJSON = lambda s: json_encode(s) 27 | Message.ParseFromJSON = lambda s, j: [s.Clear(), 1] and s.CopyFrom(json_decode(s, j)) 28 | 29 | def _load_simplejson(): 30 | try: 31 | from simplejson import dumps, loads 32 | return dumps, loads 33 | except ImportError: 34 | return None, None 35 | 36 | def _load_cjson(): 37 | try: 38 | from cjson import encode, decode 39 | _encode = encode 40 | encode = lambda *a: _encode(*a).replace('\\/', '/') #XXX: Workaround #593302 41 | return encode, decode 42 | except ImportError: 43 | return None, None 44 | 45 | _j_encode, _j_decode = None, None 46 | 47 | def _load(): 48 | global _j_encode, _j_decode 49 | if _j_encode: 50 | return 51 | _j_encode, _j_decode = _load_cjson() 52 | if _j_encode: 53 | return 54 | _j_encode, _j_decode = _load_simplejson() 55 | if not _j_encode: 56 | raise ImportError("No JSON implementation found") 57 | 58 | def json_encode(obj): 59 | """ Encode Protobuf object (Message) in JSON """ 60 | _load() 61 | return _j_encode(pb2jd(obj)) 62 | 63 | def json_decode(obj, str): 64 | """ Decode JSON message """ 65 | _load() 66 | return jd2pb(obj.DESCRIPTOR, _j_decode(str or '{}')) 67 | 68 | def pb2jd(o): 69 | def _pbe(d, obj): 70 | if d.type == d.TYPE_MESSAGE: 71 | return pb2jd(obj) 72 | elif d.type == d.TYPE_BYTES: 73 | return obj.encode('base64') 74 | else: 75 | return obj 76 | 77 | d = {} 78 | for f in o.DESCRIPTOR.fields: 79 | if f.label == f.LABEL_REPEATED: 80 | r = [] 81 | for x in getattr(o, f.name): 82 | r.append(_pbe(f, x)) 83 | d[f.name] = r 84 | elif f.label == f.LABEL_OPTIONAL: 85 | if o.HasField(f.name): 86 | d[f.name] = _pbe(f, getattr(o, f.name)) 87 | elif f.label == f.LABEL_REQUIRED: 88 | if not o.HasField(f.name): 89 | raise ValueError("Required field %s is not found" % (f.name)) 90 | d[f.name] = _pbe(f, getattr(o, f.name)) 91 | else: 92 | raise TypeError("Unknown label type %s in %s" % (f.label, f.full_name)) 93 | return d 94 | 95 | def jd2pb(descriptor, jdict): 96 | def _pbd(d, obj): 97 | if d.type == d.TYPE_MESSAGE: 98 | return jd2pb(d.message_type, obj) 99 | elif d.type == d.TYPE_BYTES: 100 | return obj.decode('base64') 101 | else: 102 | return obj 103 | 104 | o = descriptor._concrete_class() 105 | for f in descriptor.fields: 106 | if f.label == f.LABEL_REPEATED: 107 | for x in jdict.get(f.name, []): 108 | if f.type == f.TYPE_MESSAGE: 109 | getattr(o, f.name).add().CopyFrom(_pbd(f, x)) 110 | else: 111 | getattr(o, f.name).append(_pbd(f, x)) 112 | elif f.label in [f.LABEL_OPTIONAL, f.LABEL_REQUIRED]: 113 | if f.name not in jdict: 114 | if f.label == f.LABEL_REQUIRED: 115 | raise ValueError("Required field %s is not found" % (f.name)) 116 | continue 117 | x = _pbd(f, jdict[f.name]) 118 | if f.type == f.TYPE_MESSAGE: 119 | getattr(o, f.name).CopyFrom(x) 120 | else: 121 | setattr(o, f.name, x) 122 | else: 123 | raise TypeError("Unknown label type %s in %s" % (f.label, f.full_name)) 124 | return o 125 | 126 | -------------------------------------------------------------------------------- /test.json: -------------------------------------------------------------------------------- 1 | { 2 | "_str": "b", 3 | "_bin": "0a0a0a0a", 4 | "_bool": true, 5 | "_float": 1, 6 | "sub": { 7 | "field": "subfield", 8 | "echo": [ 9 | {"text": "first"}, 10 | {"text": "second"} 11 | ] 12 | }, 13 | "_int": [10, 20, 30, 40], 14 | "_enum": ["VALUE1", 20], 15 | "str_list":["v0", "v1"], 16 | "test.e_bool":false 17 | } 18 | 19 | -------------------------------------------------------------------------------- /test.proto: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | message EchoRequest { 4 | required string text = 1; 5 | } 6 | 7 | message ComplexMessage { 8 | required string _str = 1; 9 | optional float _float = 2; 10 | repeated sint64 _int = 3; 11 | required bytes _bin = 4; 12 | required bool _bool = 5; 13 | message SubMessage { 14 | required string field = 1; 15 | repeated EchoRequest echo = 2; 16 | } 17 | optional SubMessage sub = 10; 18 | enum SubEnum { 19 | VALUE1 = 10; 20 | VALUE2 = 20; 21 | }; 22 | repeated SubEnum _enum = 11; 23 | repeated string str_list = 12; 24 | extensions 100 to 199; 25 | } 26 | 27 | extend ComplexMessage { 28 | optional bool e_bool = 101; 29 | } 30 | 31 | // vim: sts=4 sw=4 et 32 | -------------------------------------------------------------------------------- /test_json.cc: -------------------------------------------------------------------------------- 1 | #include "json2pb.h" 2 | #include "test.pb.h" 3 | 4 | #include 5 | #include 6 | 7 | using google::protobuf::Message; 8 | 9 | int main() 10 | { 11 | char buf[8192]; 12 | FILE * fp = fopen("test.json", "r"); 13 | size_t size = fread(buf, 1, 8192, fp); 14 | fclose(fp); 15 | 16 | test::ComplexMessage msg; 17 | json2pb(msg, buf, size); 18 | //msg.SetExtension(test::e_bool, false); 19 | printf("Message: %s\n", msg.DebugString().c_str()); 20 | printf("JSON: %s\n", pb2json(msg).c_str()); 21 | } 22 | --------------------------------------------------------------------------------