├── .gitignore ├── .gitmodules ├── .travis.yml ├── AUTHORS.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── examples ├── benchtest.py └── simple.py ├── pyrapidjson └── _pyrapidjson.cpp ├── setup.py └── test ├── test_pyrapidjson.py └── testall.py /.gitignore: -------------------------------------------------------------------------------- 1 | MANIFEST 2 | build 3 | dist 4 | *.pyc 5 | *.egg-info 6 | *.sw? 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pyrapidjson/rapidjson"] 2 | path = pyrapidjson/rapidjson 3 | url = https://github.com/miloyip/rapidjson.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | cache: 4 | directories: 5 | - $HOME/.cache/pip 6 | - $HOME/.ccache 7 | env: 8 | global: 9 | - PATH: /usr/lib/ccache:$PATH 10 | python: 11 | - "2.6" 12 | - "2.7" 13 | - "3.4" 14 | - "3.5" 15 | - "3.6" 16 | #- "pypy" 17 | 18 | install: 19 | - pip install . 20 | 21 | script: 22 | - make test 23 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Main contributors 2 | ----------------- 3 | - Hideo Hattori (https://github.com/hhatto) 4 | 5 | Patches 6 | ------- 7 | - alingse (https://github.com/alingse) 8 | - Omer Katz (https://github.com/thedrow) 9 | - glaweh (https://github.com/glaweh) 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 Hideo Hattori 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include README.rst 3 | include Makefile 4 | include LICENSE 5 | recursive-include pyrapidjson * 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | make clean 3 | easy_install -ZU . 4 | 5 | .PHONY: test 6 | test: 7 | python test/testall.py 8 | 9 | clean: 10 | rm -rf build *.egg-info dist temp 11 | rm -rf test/*.pyc 12 | 13 | pypireg: 14 | python setup.py register 15 | python setup.py sdist upload 16 | 17 | setup: 18 | git submodule init 19 | git submodule update 20 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | pyrapidjson 2 | =========== 3 | .. image:: https://travis-ci.org/hhatto/pyrapidjson.png?branch=master 4 | :target: https://travis-ci.org/hhatto/pyrapidjson 5 | :alt: Build status 6 | 7 | 8 | About 9 | ----- 10 | pyrapidjson is a wrapper for `rapidjson`_ (JSON parser/generator). 11 | 12 | .. _`rapidjson`: https://github.com/miloyip/rapidjson 13 | 14 | 15 | Installation 16 | ------------ 17 | from pip:: 18 | 19 | $ pip install pyrapidjson 20 | 21 | from easy_install:: 22 | 23 | easy_install -ZU pyrapidjson 24 | 25 | 26 | Requirements 27 | ------------ 28 | Python2.7+ 29 | 30 | 31 | Usage 32 | ----- 33 | 34 | basic usage:: 35 | 36 | >>> import rapidjson 37 | >>> rapidjson.loads('[1, 2, {"test": "hoge"}]') 38 | >>> [1, 2, {"test": "hoge"}] 39 | >>> rapidjson.dumps([1, 2, {"foo": "bar"}]) 40 | '[1,2,{"foo":"bar"}]' 41 | >>> 42 | 43 | 44 | Links 45 | ----- 46 | * PyPI_ 47 | * GitHub_ 48 | * `Travis-CI`_ 49 | 50 | .. _PyPI: http://pypi.python.org/pypi/pyrapidjson/ 51 | .. _GitHub: https://github.com/hhatto/pyrapidjson 52 | .. _`Travis-CI`: https://travis-ci.org/hhatto/pyrapidjson 53 | 54 | -------------------------------------------------------------------------------- /examples/benchtest.py: -------------------------------------------------------------------------------- 1 | import rapidjson 2 | import json 3 | 4 | masterobj = { 5 | "web-app": { 6 | "servlet": [ 7 | { 8 | "servlet-name": "cofaxCDS", 9 | "servlet-class": "org.cofax.cds.CDSServlet", 10 | "init-param": { 11 | "configGlossary:installationAt": "Philadelphia, PA", 12 | "configGlossary:adminEmail": "ksm@pobox.com", 13 | "configGlossary:poweredBy": "Cofax", 14 | "configGlossary:poweredByIcon": "/images/cofax.gif", 15 | "configGlossary:staticPath": "/content/static", 16 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 17 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 18 | "templatePath": "templates", 19 | "templateOverridePath": "", 20 | "defaultListTemplate": "listTemplate.htm", 21 | "defaultFileTemplate": "articleTemplate.htm", 22 | "useJSP": False, 23 | "jspListTemplate": "listTemplate.jsp", 24 | "jspFileTemplate": "articleTemplate.jsp", 25 | "cachePackageTagsTrack": 200, 26 | "cachePackageTagsStore": 200, 27 | "cachePackageTagsRefresh": 60, 28 | "cacheTemplatesTrack": 100, 29 | "cacheTemplatesStore": 50, 30 | "cacheTemplatesRefresh": 15, 31 | "cachePagesTrack": 200, 32 | "cachePagesStore": 100, 33 | "cachePagesRefresh": 10, 34 | "cachePagesDirtyRead": 10, 35 | "searchEngineListTemplate": "forSearchEnginesList.htm", 36 | "searchEngineFileTemplate": "forSearchEngines.htm", 37 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 38 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 39 | "dataStoreUser": "sa", 40 | "dataStorePassword": "dataStoreTestQuery", 41 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 42 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 43 | "dataStoreInitConns": 10, 44 | "dataStoreMaxConns": 100, 45 | "dataStoreConnUsageLimit": 100, 46 | "dataStoreLogLevel": "debug", 47 | "maxUrlLength": 500 48 | } 49 | }, 50 | { 51 | "servlet-name": "cofaxEmail", 52 | "servlet-class": "org.cofax.cds.EmailServlet", 53 | "init-param": { 54 | "mailHost": "mail1", 55 | "mailHostOverride": "mail2" 56 | } 57 | }, 58 | { 59 | "servlet-name": "cofaxAdmin", 60 | "servlet-class": "org.cofax.cds.AdminServlet" 61 | }, 62 | { 63 | "servlet-name": "fileServlet", 64 | "servlet-class": "org.cofax.cds.FileServlet" 65 | }, 66 | { 67 | "servlet-name": "cofaxTools", 68 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 69 | "init-param": { 70 | "templatePath": "toolstemplates/", 71 | "log": 1, 72 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 73 | "logMaxSize": "", 74 | "dataLog": 1, 75 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 76 | "dataLogMaxSize": "", 77 | "removePageCache": "/content/admin/remove?cache=pages&id=", 78 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 79 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 80 | "lookInContext": 1, 81 | "adminGroupID": 4, 82 | "betaServer": True 83 | } 84 | } 85 | ], 86 | "servlet-mapping": { 87 | "cofaxCDS": "/", 88 | "cofaxEmail": "/cofaxutil/aemail/*", 89 | "cofaxAdmin": "/admin/*", 90 | "fileServlet": "/static/*", 91 | "cofaxTools": "/tools/*" 92 | }, 93 | "taglib": { 94 | "taglib-uri": "cofax.tld", 95 | "taglib-location": "/WEB-INF/tlds/cofax.tld" 96 | } 97 | } 98 | } 99 | jsonstr = """ 100 | { 101 | "web-app": { 102 | "servlet": [ 103 | { 104 | "servlet-name": "cofaxCDS", 105 | "servlet-class": "org.cofax.cds.CDSServlet", 106 | "init-param": { 107 | "configGlossary:installationAt": "Philadelphia, PA", 108 | "configGlossary:adminEmail": "ksm@pobox.com", 109 | "configGlossary:poweredBy": "Cofax", 110 | "configGlossary:poweredByIcon": "/images/cofax.gif", 111 | "configGlossary:staticPath": "/content/static", 112 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 113 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 114 | "templatePath": "templates", 115 | "templateOverridePath": "", 116 | "defaultListTemplate": "listTemplate.htm", 117 | "defaultFileTemplate": "articleTemplate.htm", 118 | "useJSP": false, 119 | "jspListTemplate": "listTemplate.jsp", 120 | "jspFileTemplate": "articleTemplate.jsp", 121 | "cachePackageTagsTrack": 200, 122 | "cachePackageTagsStore": 200, 123 | "cachePackageTagsRefresh": 60, 124 | "cacheTemplatesTrack": 100, 125 | "cacheTemplatesStore": 50, 126 | "cacheTemplatesRefresh": 15, 127 | "cachePagesTrack": 200, 128 | "cachePagesStore": 100, 129 | "cachePagesRefresh": 10, 130 | "cachePagesDirtyRead": 10, 131 | "searchEngineListTemplate": "forSearchEnginesList.htm", 132 | "searchEngineFileTemplate": "forSearchEngines.htm", 133 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 134 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 135 | "dataStoreUser": "sa", 136 | "dataStorePassword": "dataStoreTestQuery", 137 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 138 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 139 | "dataStoreInitConns": 10, 140 | "dataStoreMaxConns": 100, 141 | "dataStoreConnUsageLimit": 100, 142 | "dataStoreLogLevel": "debug", 143 | "maxUrlLength": 500 144 | } 145 | }, 146 | { 147 | "servlet-name": "cofaxEmail", 148 | "servlet-class": "org.cofax.cds.EmailServlet", 149 | "init-param": { 150 | "mailHost": "mail1", 151 | "mailHostOverride": "mail2" 152 | } 153 | }, 154 | { 155 | "servlet-name": "cofaxAdmin", 156 | "servlet-class": "org.cofax.cds.AdminServlet" 157 | }, 158 | { 159 | "servlet-name": "fileServlet", 160 | "servlet-class": "org.cofax.cds.FileServlet" 161 | }, 162 | { 163 | "servlet-name": "cofaxTools", 164 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 165 | "init-param": { 166 | "templatePath": "toolstemplates/", 167 | "log": 1, 168 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 169 | "logMaxSize": "", 170 | "dataLog": 1, 171 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 172 | "dataLogMaxSize": "", 173 | "removePageCache": "/content/admin/remove?cache=pages&id=", 174 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 175 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 176 | "lookInContext": 1, 177 | "adminGroupID": 4, 178 | "betaServer": true 179 | } 180 | } 181 | ], 182 | "servlet-mapping": { 183 | "cofaxCDS": "/", 184 | "cofaxEmail": "/cofaxutil/aemail/*", 185 | "cofaxAdmin": "/admin/*", 186 | "fileServlet": "/static/*", 187 | "cofaxTools": "/tools/*" 188 | }, 189 | "taglib": { 190 | "taglib-uri": "cofax.tld", 191 | "taglib-location": "/WEB-INF/tlds/cofax.tld" 192 | } 193 | } 194 | } 195 | """ 196 | jsonobj = rapidjson.loads(jsonstr) 197 | if jsonobj != masterobj: 198 | print("error!!") 199 | print(len(jsonobj), len(masterobj)) 200 | print(jsonobj) 201 | print(masterobj) 202 | #jsonobj = json.loads(jsonstr) 203 | #if jsonobj != masterobj: 204 | # print "error!!" 205 | -------------------------------------------------------------------------------- /examples/simple.py: -------------------------------------------------------------------------------- 1 | import rapidjson 2 | 3 | jsonstr = """\ 4 | [ 5 | "name", 6 | [1, 3, true], 7 | {"windows": "XP", "linux": null} 8 | ]""" 9 | print(rapidjson.loads(jsonstr)) 10 | 11 | print("=" * 30) 12 | jsonstr = """\ 13 | { 14 | "name" : "Jack", 15 | "bar" : false, 16 | "foo" : true, 17 | "age" : 20, 18 | "length" : 65.97, 19 | "pc" : {"windows": "XP", "linux": null} 20 | }""" 21 | print(rapidjson.loads(jsonstr)) 22 | 23 | print("=" * 30) 24 | print(rapidjson.loads("true")) 25 | 26 | print("=" * 30) 27 | print(rapidjson.loads("null")) 28 | 29 | print("=" * 30) 30 | print(rapidjson.loads("2.14")) 31 | 32 | print("=" * 30) 33 | print(rapidjson.dumps([1, None])) 34 | 35 | print("=" * 30) 36 | json = { 37 | "name" : "Jack", 38 | "bar" : False, 39 | "foo" : True, 40 | "age" : 20, 41 | "length" : 65.97, 42 | "pc" : {"windows": "XP", "linux": None} 43 | } 44 | print(rapidjson.dumps(json)) 45 | -------------------------------------------------------------------------------- /pyrapidjson/_pyrapidjson.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "rapidjson/rapidjson.h" 7 | #include "rapidjson/error/en.h" 8 | #include "rapidjson/document.h" 9 | #include "rapidjson/prettywriter.h" 10 | #include "rapidjson/stringbuffer.h" 11 | #include "rapidjson/filereadstream.h" 12 | #include "rapidjson/filewritestream.h" 13 | #include "rapidjson/encodedstream.h" 14 | 15 | #if PY_MAJOR_VERSION >= 3 16 | #define PY3 17 | #define PyInt_FromLong PyLong_FromLong 18 | #define PyInt_FromString PyLong_FromString 19 | #define PyInt_Check PyLong_Check 20 | #define PyString_FromStringAndSize PyUnicode_FromStringAndSize 21 | #define PyString_Check PyBytes_Check 22 | #define PyString_AsString PyBytes_AsString 23 | #define PyString_GET_SIZE PyUnicode_GET_SIZE 24 | #endif 25 | 26 | #ifdef DEBUG 27 | #define _debug(format,...) printf("[DEBUG]" format,__VA_ARGS__) 28 | #define _info(format,...) printf("[INFO]" format,__VA_ARGS__) 29 | #endif 30 | 31 | // for Python2.6 32 | #ifndef _PyVerify_fd 33 | #define _PyVerify_fd(FD) (1) 34 | #endif 35 | 36 | static bool pyobj2doc(PyObject *object, rapidjson::Document& doc); 37 | static bool pyobj2doc(PyObject *object, rapidjson::Value& doc, rapidjson::Document& root); 38 | 39 | static PyObject* _doc2pyobj(rapidjson::Document&, char *); 40 | static PyObject* doc2pyobj(rapidjson::Document&); 41 | static PyObject* _get_pyobj_from_object( 42 | rapidjson::Value::ConstMemberIterator& doc, PyObject *root, 43 | const char *key); 44 | 45 | /* The module doc strings */ 46 | PyDoc_STRVAR(pyrapidjson__doc__, "Python binding for rapidjson"); 47 | PyDoc_STRVAR(pyrapidjson_loads__doc__, "Decoding JSON"); 48 | PyDoc_STRVAR(pyrapidjson_load__doc__, "Decoding JSON file like object"); 49 | PyDoc_STRVAR(pyrapidjson_dumps__doc__, "Encoding JSON"); 50 | PyDoc_STRVAR(pyrapidjson_dump__doc__, "Encoding JSON file like object"); 51 | 52 | 53 | static inline bool 54 | pyobj2doc_pair(PyObject *key, PyObject *value, 55 | rapidjson::Value& doc, rapidjson::Document& root) 56 | { 57 | const char *key_string; 58 | #ifdef PY3 59 | PyObject *utf8_item; 60 | utf8_item = PyUnicode_AsUTF8String(key); 61 | key_string = PyBytes_AsString(utf8_item); 62 | #else 63 | key_string = PyString_AsString(key); 64 | #endif 65 | rapidjson::Value s; 66 | s.SetString(key_string, root.GetAllocator()); 67 | rapidjson::Value _v; 68 | if (false == pyobj2doc(value, _v, root)) { 69 | return false; 70 | } 71 | doc.AddMember(s, _v, root.GetAllocator()); 72 | return true; 73 | } 74 | static inline bool 75 | pyobj2doc_pair(PyObject *key, PyObject *value, rapidjson::Document& doc) 76 | { 77 | const char *key_string; 78 | PyObject *utf8_item = NULL; 79 | PyObject *pyobj = NULL; 80 | #ifdef PY3 81 | if (!PyUnicode_Check(key)) { 82 | pyobj = PyObject_Str(key); 83 | if (pyobj == NULL) { 84 | PyErr_SetString(PyExc_TypeError, "not support key type"); 85 | return false; 86 | } 87 | utf8_item = PyUnicode_AsUTF8String(pyobj); 88 | } else { 89 | utf8_item = PyUnicode_AsUTF8String(key); 90 | } 91 | key_string = PyBytes_AsString(utf8_item); 92 | #else 93 | if (PyString_Check(key)) { 94 | key_string = PyString_AsString(key); 95 | } else if (PyUnicode_Check(key)) { 96 | utf8_item = PyUnicode_AsUTF8String(key); 97 | key_string = PyBytes_AsString(utf8_item); 98 | } else { 99 | PyObject *pyobj = PyObject_Str(key); 100 | if (pyobj == NULL) { 101 | PyErr_SetString(PyExc_TypeError, "not support key type"); 102 | return false; 103 | } 104 | Py_DECREF(key); 105 | key_string = PyString_AsString(pyobj); 106 | } 107 | #endif 108 | rapidjson::Value s; 109 | s.SetString(key_string, doc.GetAllocator()); 110 | 111 | Py_XDECREF(pyobj); 112 | Py_XDECREF(utf8_item); 113 | 114 | rapidjson::Value _v; 115 | if (false == pyobj2doc(value, _v, doc)) { 116 | return false; 117 | } 118 | doc.AddMember(s, _v, doc.GetAllocator()); 119 | return true; 120 | } 121 | 122 | static inline void 123 | _set_pyobj(PyObject *parent, PyObject *child, const char *key) 124 | { 125 | if (child && !parent) { 126 | return; 127 | } 128 | 129 | if (PyList_Check(parent)) { 130 | PyList_Append(parent, child); 131 | if (child && child != Py_None) { 132 | Py_XDECREF(child); 133 | } 134 | } 135 | else if (PyDict_Check(parent)) { 136 | #ifdef PY3 137 | PyDict_SetItemString(parent, key, child); 138 | #else 139 | PyObject *utf8key; 140 | utf8key = PyUnicode_FromString(key); 141 | if (!utf8key) { 142 | return; 143 | } 144 | PyDict_SetItem(parent, utf8key, child); 145 | Py_DECREF(utf8key); 146 | #endif 147 | if (child && child != Py_None) { 148 | Py_XDECREF(child); 149 | } 150 | } 151 | } 152 | 153 | static PyObject * 154 | _get_pyobj_from_array(rapidjson::Value::ConstValueIterator& doc, 155 | PyObject *root) 156 | { 157 | PyObject *obj, *utf8item; 158 | 159 | switch (doc->GetType()) { 160 | case rapidjson::kObjectType: 161 | obj = PyDict_New(); 162 | for (rapidjson::Value::ConstMemberIterator itr = doc->MemberBegin(); 163 | itr != doc->MemberEnd(); ++itr) { 164 | _get_pyobj_from_object(itr, obj, itr->name.GetString()); 165 | } 166 | break; 167 | case rapidjson::kArrayType: 168 | obj = PyList_New(0); 169 | for (rapidjson::Value::ConstValueIterator itr = doc->Begin(); 170 | itr != doc->End(); ++itr) { 171 | _get_pyobj_from_array(itr, obj); 172 | } 173 | break; 174 | case rapidjson::kTrueType: 175 | obj = PyBool_FromLong(1); 176 | break; 177 | case rapidjson::kFalseType: 178 | obj = PyBool_FromLong(0); 179 | break; 180 | case rapidjson::kStringType: 181 | #ifdef PY3 182 | obj = PyString_FromStringAndSize(doc->GetString(), 183 | doc->GetStringLength()); 184 | #else 185 | utf8item = PyUnicode_FromStringAndSize(doc->GetString(), 186 | doc->GetStringLength()); 187 | if (utf8item) { 188 | obj = utf8item; 189 | } else { 190 | obj = PyString_FromStringAndSize(doc->GetString(), 191 | doc->GetStringLength()); 192 | } 193 | #endif 194 | break; 195 | case rapidjson::kNumberType: 196 | if (doc->IsDouble()) { 197 | obj = PyFloat_FromDouble(doc->GetDouble()); 198 | } 199 | else { 200 | obj = PyInt_FromLong(doc->GetInt64()); 201 | } 202 | break; 203 | case rapidjson::kNullType: 204 | Py_INCREF(Py_None); 205 | obj = Py_None; 206 | break; 207 | default: 208 | PyErr_SetString(PyExc_RuntimeError, "not support type"); 209 | return NULL; 210 | } 211 | 212 | _set_pyobj(root, obj, NULL); 213 | 214 | return root; 215 | } 216 | 217 | static PyObject * 218 | _get_pyobj_from_object(rapidjson::Value::ConstMemberIterator& doc, 219 | PyObject *root, 220 | const char *key) 221 | { 222 | PyObject *obj, *utf8item; 223 | 224 | switch (doc->value.GetType()) { 225 | case rapidjson::kObjectType: 226 | obj = PyDict_New(); 227 | for (rapidjson::Value::ConstMemberIterator itr = doc->value.MemberBegin(); 228 | itr != doc->value.MemberEnd(); ++itr) { 229 | _get_pyobj_from_object(itr, obj, itr->name.GetString()); 230 | } 231 | break; 232 | case rapidjson::kArrayType: 233 | obj = PyList_New(0); 234 | for (rapidjson::Value::ConstValueIterator itr = doc->value.Begin(); 235 | itr != doc->value.End(); ++itr) { 236 | _get_pyobj_from_array(itr, obj); 237 | } 238 | break; 239 | case rapidjson::kTrueType: 240 | obj = PyBool_FromLong(1); 241 | break; 242 | case rapidjson::kFalseType: 243 | obj = PyBool_FromLong(0); 244 | break; 245 | case rapidjson::kStringType: 246 | #ifdef PY3 247 | obj = PyString_FromStringAndSize(doc->value.GetString(), 248 | doc->value.GetStringLength()); 249 | #else 250 | utf8item = PyUnicode_FromStringAndSize(doc->value.GetString(), 251 | doc->value.GetStringLength()); 252 | if (utf8item) { 253 | obj = utf8item; 254 | } else { 255 | obj = PyString_FromStringAndSize(doc->value.GetString(), 256 | doc->value.GetStringLength()); 257 | } 258 | #endif 259 | break; 260 | case rapidjson::kNumberType: 261 | if (doc->value.IsDouble()) { 262 | obj = PyFloat_FromDouble(doc->value.GetDouble()); 263 | } 264 | else { 265 | obj = PyInt_FromLong(doc->value.GetInt64()); 266 | } 267 | break; 268 | case rapidjson::kNullType: 269 | Py_INCREF(Py_None); 270 | obj = Py_None; 271 | break; 272 | default: 273 | PyErr_SetString(PyExc_RuntimeError, "not support type"); 274 | return NULL; 275 | } 276 | 277 | _set_pyobj(root, obj, key); 278 | return obj; 279 | } 280 | 281 | static PyObject * 282 | doc2pyobj(rapidjson::Document& doc) 283 | { 284 | PyObject *root; 285 | 286 | if (doc.IsArray()) { 287 | root = PyList_New(0); 288 | for (rapidjson::Value::ConstValueIterator itr = doc.Begin(); 289 | itr != doc.End(); ++itr) { 290 | _get_pyobj_from_array(itr, root); 291 | } 292 | } 293 | else { 294 | // Object 295 | root = PyDict_New(); 296 | for (rapidjson::Value::ConstMemberIterator itr = doc.MemberBegin(); 297 | itr != doc.MemberEnd(); ++itr) { 298 | _get_pyobj_from_object(itr, root, itr->name.GetString()); 299 | } 300 | } 301 | 302 | return root; 303 | } 304 | 305 | static bool 306 | pyobj2doc(PyObject *object, rapidjson::Value& doc, rapidjson::Document& root) 307 | { 308 | if (PyBool_Check(object)) { 309 | if (Py_True == object) { 310 | doc.SetBool(true); 311 | } 312 | else { 313 | doc.SetBool(false); 314 | } 315 | } 316 | else if (Py_None == object) { 317 | doc.SetNull(); 318 | } 319 | else if (PyFloat_Check(object)) { 320 | doc.SetDouble(PyFloat_AsDouble(object)); 321 | } 322 | else if (PyInt_Check(object)) { 323 | doc.SetInt64(PyLong_AsLong(object)); 324 | } 325 | else if (PyString_Check(object)) { 326 | doc.SetString(PyString_AsString(object), PyString_GET_SIZE(object)); 327 | } 328 | else if (PyUnicode_Check(object)) { 329 | PyObject *utf8_item = PyUnicode_AsUTF8String(object); 330 | if (!utf8_item) { 331 | PyErr_SetString(PyExc_RuntimeError, "codec error."); 332 | return false; 333 | } 334 | #ifdef PY3 335 | doc.SetString(PyBytes_AsString(utf8_item), PyBytes_GET_SIZE(utf8_item), root.GetAllocator()); 336 | #else 337 | doc.SetString(PyString_AsString(utf8_item), PyString_GET_SIZE(utf8_item), root.GetAllocator()); 338 | #endif 339 | Py_XDECREF(utf8_item); 340 | } 341 | else if (PyTuple_Check(object)) { 342 | int len = PyTuple_Size(object), i; 343 | 344 | doc.SetArray(); 345 | rapidjson::Value _v; 346 | for (i = 0; i < len; ++i) { 347 | PyObject *elm = PyTuple_GetItem(object, i); 348 | if (false == pyobj2doc(elm, _v, root)) { 349 | return false; 350 | } 351 | doc.PushBack(_v, root.GetAllocator()); 352 | } 353 | } 354 | else if (PyList_Check(object)) { 355 | int len = PyList_Size(object), i; 356 | 357 | doc.SetArray(); 358 | rapidjson::Value _v; 359 | for (i = 0; i < len; ++i) { 360 | PyObject *elm = PyList_GetItem(object, i); 361 | if (false == pyobj2doc(elm, _v, root)) { 362 | return false; 363 | } 364 | doc.PushBack(_v, root.GetAllocator()); 365 | } 366 | } 367 | else if (PyDict_Check(object)) { 368 | doc.SetObject(); 369 | PyObject *key, *value; 370 | Py_ssize_t pos = 0; 371 | while (PyDict_Next(object, &pos, &key, &value)) { 372 | if (false == pyobj2doc_pair(key, value, doc, root)) { 373 | return false; 374 | } 375 | } 376 | } 377 | else { 378 | PyErr_SetString(PyExc_RuntimeError, "invalid python object"); 379 | return false; 380 | } 381 | 382 | return true; 383 | } 384 | 385 | static bool 386 | pyobj2doc(PyObject *object, rapidjson::Document& doc) 387 | { 388 | if (PyBool_Check(object)) { 389 | if (Py_True == object) { 390 | doc.SetBool(true); 391 | } 392 | else { 393 | doc.SetBool(false); 394 | } 395 | } 396 | else if (Py_None == object) { 397 | doc.SetNull(); 398 | } 399 | else if (PyFloat_Check(object)) { 400 | doc.SetDouble(PyFloat_AsDouble(object)); 401 | } 402 | else if (PyInt_Check(object)) { 403 | doc.SetInt64(PyLong_AsLong(object)); 404 | } 405 | else if (PyString_Check(object)) { 406 | doc.SetString(PyString_AsString(object), PyString_GET_SIZE(object)); 407 | } 408 | else if (PyUnicode_Check(object)) { 409 | PyObject *utf8_item; 410 | utf8_item = PyUnicode_AsUTF8String(object); 411 | if (!utf8_item) { 412 | PyErr_SetString(PyExc_RuntimeError, "codec error."); 413 | return false; 414 | } 415 | #ifdef PY3 416 | doc.SetString(PyBytes_AsString(utf8_item), PyBytes_GET_SIZE(utf8_item), doc.GetAllocator()); 417 | #else 418 | doc.SetString(PyString_AsString(utf8_item), PyString_GET_SIZE(utf8_item), doc.GetAllocator()); 419 | #endif 420 | Py_XDECREF(utf8_item); 421 | } 422 | else if (PyTuple_Check(object)) { 423 | int len = PyTuple_Size(object), i; 424 | doc.SetArray(); 425 | rapidjson::Value _v; 426 | for (i = 0; i < len; ++i) { 427 | PyObject *elm = PyTuple_GetItem(object, i); 428 | if (false == pyobj2doc(elm, _v, doc)) { 429 | return false; 430 | } 431 | doc.PushBack(_v, doc.GetAllocator()); 432 | } 433 | } 434 | else if (PyList_Check(object)) { 435 | int len = PyList_Size(object), i; 436 | doc.SetArray(); 437 | rapidjson::Value _v; 438 | for (i = 0; i < len; ++i) { 439 | PyObject *elm = PyList_GetItem(object, i); 440 | if (false == pyobj2doc(elm, _v, doc)) { 441 | return false; 442 | } 443 | doc.PushBack(_v, doc.GetAllocator()); 444 | } 445 | } 446 | else if (PyDict_Check(object)) { 447 | doc.SetObject(); 448 | PyObject *key, *value; 449 | Py_ssize_t pos = 0; 450 | while (PyDict_Next(object, &pos, &key, &value)) { 451 | if (false == pyobj2doc_pair(key, value, doc)) { 452 | return false; 453 | } 454 | } 455 | } 456 | else { 457 | PyErr_SetString(PyExc_RuntimeError, "invalid python object"); 458 | return false; 459 | } 460 | 461 | return true; 462 | } 463 | 464 | static PyObject * 465 | pyobj2pystring(PyObject *pyjson) 466 | { 467 | rapidjson::Document doc; 468 | 469 | if (false == pyobj2doc(pyjson, doc)) { 470 | return NULL; 471 | } 472 | 473 | rapidjson::StringBuffer buffer; 474 | rapidjson::Writer > writer(buffer); 475 | doc.Accept(writer); 476 | std::string s = buffer.GetString(); 477 | 478 | return PyString_FromStringAndSize(s.data(), s.length()); 479 | } 480 | 481 | static PyObject * 482 | _doc2pyobj(rapidjson::Document& doc, char *text) 483 | { 484 | int is_float; 485 | unsigned int offset; 486 | PyObject *pyjson; 487 | 488 | if (!(doc.IsArray() || doc.IsObject())) { 489 | switch (text[0]) { 490 | case 't': 491 | return PyBool_FromLong(1); 492 | case 'f': 493 | return PyBool_FromLong(0); 494 | case 'n': 495 | Py_RETURN_NONE; 496 | case '"': 497 | #ifdef PY3 498 | return PyString_FromStringAndSize(text + 1, strlen(text) - 2); 499 | #else 500 | PyObject *utf8item; 501 | utf8item = PyUnicode_FromStringAndSize(text + 1, strlen(text) - 2); 502 | if (utf8item) { 503 | return utf8item; 504 | } else { 505 | return PyString_FromStringAndSize(text + 1, strlen(text) - 2); 506 | } 507 | #endif 508 | default: 509 | is_float = 0; 510 | for (offset = 0; offset < strlen(text); offset++) { 511 | switch (text[offset]) { 512 | case '.': 513 | case 'e': 514 | case 'E': 515 | is_float = 1; 516 | break; 517 | } 518 | if (is_float) break; 519 | } 520 | if (is_float) { 521 | pyjson = PyFloat_FromDouble(atof(text)); 522 | } 523 | else { 524 | pyjson = PyInt_FromString(text, NULL, 10); 525 | } 526 | return pyjson; 527 | } 528 | } 529 | 530 | return doc2pyobj(doc); 531 | } 532 | 533 | static PyObject * 534 | pyrapidjson_loads(PyObject *self, PyObject *args, PyObject *kwargs) 535 | { 536 | static char *kwlist[] = {(char *)"text", NULL}; 537 | char *text; 538 | rapidjson::Document doc; 539 | 540 | /* Parse arguments */ 541 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &text)) 542 | return NULL; 543 | 544 | doc.Parse(text); 545 | if (doc.HasParseError()) { 546 | PyErr_SetString(PyExc_ValueError, GetParseError_En(doc.GetParseError())); 547 | return NULL; 548 | } 549 | 550 | return _doc2pyobj(doc, text); 551 | } 552 | 553 | static PyObject * 554 | pyrapidjson_load(PyObject *self, PyObject *args, PyObject *kwargs) 555 | { 556 | static char *kwlist[] = {(char *)"text", NULL}; 557 | PyObject *py_file, *py_string, *read_method; 558 | char *text; 559 | rapidjson::Document doc; 560 | 561 | /* Parse arguments */ 562 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &py_file)) 563 | return NULL; 564 | 565 | if (!PyObject_HasAttrString(py_file, "read")) { 566 | PyErr_Format(PyExc_TypeError, "expected file object. has not read() method."); 567 | return NULL; 568 | } 569 | read_method = PyObject_GetAttrString(py_file, "read"); 570 | if (!PyCallable_Check(read_method)) { 571 | Py_XDECREF(read_method); 572 | PyErr_Format(PyExc_TypeError, "expected file object. read() method is not callable."); 573 | return NULL; 574 | } 575 | 576 | py_string = PyObject_CallObject(read_method, NULL); 577 | if (py_string == NULL) { 578 | Py_XDECREF(read_method); 579 | return NULL; 580 | } 581 | 582 | #ifdef PY3 583 | PyObject *utf8_item; 584 | utf8_item = PyUnicode_AsUTF8String(py_string); 585 | text = PyBytes_AsString(utf8_item); 586 | #else 587 | text = PyString_AsString(py_string); 588 | #endif 589 | doc.Parse(text); 590 | 591 | if (doc.HasParseError()) { 592 | Py_XDECREF(read_method); 593 | Py_XDECREF(py_string); 594 | PyErr_SetString(PyExc_ValueError, GetParseError_En(doc.GetParseError())); 595 | return NULL; 596 | } 597 | 598 | Py_XDECREF(read_method); 599 | Py_XDECREF(py_string); 600 | 601 | PyObject *ret = _doc2pyobj(doc, text); 602 | #ifdef PY3 603 | Py_XDECREF(utf8_item); 604 | #endif 605 | return ret; 606 | } 607 | 608 | 609 | static PyObject * 610 | pyrapidjson_dumps(PyObject *self, PyObject *args, PyObject *kwargs) 611 | { 612 | static char *kwlist[] = {(char *)"obj", NULL}; 613 | PyObject *pyjson; 614 | 615 | /* Parse arguments */ 616 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &pyjson)) 617 | return NULL; 618 | 619 | return pyobj2pystring(pyjson); 620 | } 621 | 622 | 623 | static PyObject * 624 | pyrapidjson_dump(PyObject *self, PyObject *args, PyObject *kwargs) 625 | { 626 | // TODO: not support kwargs like json.dump() (encoding, etc...) 627 | static char *kwlist[] = {(char *)"obj", (char *)"fp", NULL}; 628 | PyObject *py_file, *py_json, *py_string, *write_method, *write_arg, *write_ret; 629 | rapidjson::Document doc; 630 | 631 | /* Parse arguments */ 632 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwlist, &py_json, &py_file)) 633 | return NULL; 634 | 635 | if (!PyObject_HasAttrString(py_file, "write")) { 636 | PyErr_Format(PyExc_TypeError, "expected file object. has not write() method."); 637 | return NULL; 638 | } 639 | write_method = PyObject_GetAttrString(py_file, "write"); 640 | if (!PyCallable_Check(write_method)) { 641 | Py_XDECREF(write_method); 642 | PyErr_Format(PyExc_TypeError, "expected file object. write() method is not callable."); 643 | return NULL; 644 | } 645 | 646 | py_string = pyobj2pystring(py_json); 647 | if (py_string == NULL) { 648 | Py_XDECREF(write_method); 649 | PyErr_SetString(PyExc_RuntimeError, "pyobj2pystring() error."); 650 | return NULL; 651 | } 652 | 653 | write_arg = PyTuple_Pack(1, py_string); 654 | if (write_arg == NULL) { 655 | Py_XDECREF(write_method); 656 | return NULL; 657 | } 658 | 659 | write_ret = PyObject_CallObject(write_method, write_arg); 660 | if (write_ret == NULL) { 661 | Py_XDECREF(write_method); 662 | Py_XDECREF(write_arg); 663 | Py_XDECREF(py_string); 664 | return NULL; 665 | } 666 | 667 | Py_XDECREF(write_method); 668 | Py_XDECREF(write_arg); 669 | Py_XDECREF(write_ret); 670 | Py_XDECREF(py_string); 671 | 672 | Py_RETURN_NONE; 673 | } 674 | 675 | static PyMethodDef PyrapidjsonMethods[] = { 676 | {"loads", (PyCFunction)pyrapidjson_loads, METH_VARARGS | METH_KEYWORDS, 677 | pyrapidjson_loads__doc__}, 678 | {"load", (PyCFunction)pyrapidjson_load, METH_VARARGS | METH_KEYWORDS, 679 | pyrapidjson_load__doc__}, 680 | {"dumps", (PyCFunction)pyrapidjson_dumps, METH_VARARGS | METH_KEYWORDS, 681 | pyrapidjson_dumps__doc__}, 682 | {"dump", (PyCFunction)pyrapidjson_dump, METH_VARARGS | METH_KEYWORDS, 683 | pyrapidjson_dump__doc__}, 684 | {NULL, NULL, 0, NULL} /* Sentinel */ 685 | }; 686 | 687 | #ifdef PY3 688 | static struct PyModuleDef pyrapidjson_module_def = { 689 | PyModuleDef_HEAD_INIT, 690 | "rapidjson", 691 | pyrapidjson__doc__, 692 | -1, 693 | PyrapidjsonMethods, 694 | }; 695 | 696 | PyMODINIT_FUNC 697 | PyInit_rapidjson(void) 698 | #else 699 | 700 | PyMODINIT_FUNC 701 | initrapidjson(void) 702 | #endif 703 | { 704 | PyObject *module; 705 | 706 | #ifdef PY3 707 | module = PyModule_Create(&pyrapidjson_module_def); 708 | return module; 709 | #else 710 | /* The module */ 711 | module = Py_InitModule3("rapidjson", PyrapidjsonMethods, pyrapidjson__doc__); 712 | if (module == NULL) 713 | return; 714 | #endif 715 | } 716 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | 4 | setup( 5 | name='pyrapidjson', 6 | version='0.5.1', 7 | description='Python Interface for rapidjson(JSON parser and generator).', 8 | long_description=open('README.rst').read(), 9 | license='Expat License', 10 | author='Hideo Hattori', 11 | author_email='hhatto.jp@gmail.com', 12 | url='https://github.com/hhatto/pyrapidjson', 13 | classifiers=[ 14 | 'Development Status :: 4 - Beta', 15 | 'Intended Audience :: Developers', 16 | 'License :: OSI Approved :: MIT License', 17 | 'Operating System :: OS Independent', 18 | 'Programming Language :: C++', 19 | 'Programming Language :: Python', 20 | 'Programming Language :: Python :: 2', 21 | 'Programming Language :: Python :: 3', 22 | ], 23 | keywords='json rapidjson', 24 | ext_modules=[ 25 | Extension('rapidjson', 26 | sources=['pyrapidjson/_pyrapidjson.cpp'], 27 | include_dirs=['./pyrapidjson/rapidjson/include/'], 28 | #extra_compile_args=["-DDEBUG"], 29 | )] 30 | ) 31 | -------------------------------------------------------------------------------- /test/test_pyrapidjson.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import sys 3 | import os 4 | import json 5 | import unittest 6 | from tempfile import NamedTemporaryFile 7 | import rapidjson 8 | 9 | if sys.version_info < (3, ): 10 | try: 11 | from io import BytesIO as StringIO 12 | except ImportError: 13 | from StringIO import StringIO 14 | else: 15 | from io import StringIO 16 | 17 | 18 | class TestDecodeSimple(unittest.TestCase): 19 | 20 | def test_integer(self): 21 | text = "12" 22 | ret = rapidjson.loads(text) 23 | self.assertEqual(ret, 12) 24 | 25 | def test_negative_integer(self): 26 | text = "-12" 27 | ret = rapidjson.loads(text) 28 | self.assertEqual(ret, -12) 29 | 30 | def test_float(self): 31 | text = "12.3" 32 | ret = rapidjson.loads(text) 33 | self.assertEqual(ret, 12.3) 34 | 35 | def test_negative_float(self): 36 | text = "-12.3" 37 | ret = rapidjson.loads(text) 38 | self.assertEqual(ret, -12.3) 39 | 40 | def test_string(self): 41 | text = "\"hello world\"" 42 | ret = rapidjson.loads(text) 43 | self.assertEqual(ret, "hello world") 44 | 45 | def test_true(self): 46 | text = "true" 47 | ret = rapidjson.loads(text) 48 | self.assertEqual(ret, True) 49 | 50 | def test_false(self): 51 | text = "false" 52 | ret = rapidjson.loads(text) 53 | self.assertEqual(ret, False) 54 | 55 | def test_none(self): 56 | text = "null" 57 | ret = rapidjson.loads(text) 58 | self.assertEqual(ret, None) 59 | 60 | def test_list_size_one(self): 61 | text = "[null]" 62 | ret = rapidjson.loads(text) 63 | self.assertEqual(ret, [None]) 64 | 65 | def test_list_size_two(self): 66 | text = "[false, -50.3]" 67 | ret = rapidjson.loads(text) 68 | self.assertEqual(ret, [False, -50.3]) 69 | 70 | def test_dict_size_one(self): 71 | text = """{"20":null}""" 72 | ret = rapidjson.loads(text) 73 | self.assertEqual(ret, {'20': None}) 74 | 75 | def test_dict_size_two(self): 76 | text = """{"hoge":null, "huga":134}""" 77 | ret = rapidjson.loads(text) 78 | self.assertEqual(ret, {"hoge": None, "huga": 134}) 79 | 80 | def test_unicode(self): 81 | text = '"は"' 82 | rapid_ret = rapidjson.loads(text) 83 | std_ret = json.loads(text) 84 | self.assertEqual(std_ret, u"は") 85 | self.assertEqual(rapid_ret, u"は") 86 | self.assertEqual(std_ret, rapid_ret) 87 | 88 | 89 | class TestDecodeComplex(unittest.TestCase): 90 | 91 | def test_list_in_list(self): 92 | text = """["test", [1, "hello"]]""" 93 | ret = rapidjson.loads(text) 94 | self.assertEqual(ret, ["test", [1, "hello"]]) 95 | 96 | def test_list_in_dict(self): 97 | text = """{"test": [1, "hello"]}""" 98 | ret = rapidjson.loads(text) 99 | self.assertEqual(ret, {"test": [1, "hello"]}) 100 | 101 | def test_dict_in_dict(self): 102 | text = """{"test": {"hello": "world"}}""" 103 | ret = rapidjson.loads(text) 104 | self.assertEqual(ret, {"test": {"hello": "world"}}) 105 | 106 | def test_dict_with_unicode(self): 107 | text = '''{"は": "bc"}''' 108 | ret = rapidjson.loads(text) 109 | self.assertEqual(ret, {u"は": u"bc"}) 110 | 111 | 112 | class TestDecodeFail(unittest.TestCase): 113 | 114 | def test_use_single_quote_for_string(self): 115 | text = "'foo'" 116 | self.assertRaises(ValueError, rapidjson.loads, text) 117 | 118 | 119 | class TestEncodeSimple(unittest.TestCase): 120 | 121 | def test_integer(self): 122 | jsonobj = 1 123 | ret = rapidjson.dumps(jsonobj) 124 | self.assertEqual(ret, "1") 125 | 126 | def test_negative_integer(self): 127 | jsonobj = -100 128 | ret = rapidjson.dumps(jsonobj) 129 | self.assertEqual(ret, "-100") 130 | 131 | def test_float(self): 132 | jsonobj = 12.3 133 | ret = rapidjson.dumps(jsonobj) 134 | self.assertEqual(ret, "12.3") 135 | 136 | def test_negative_float(self): 137 | jsonobj = -12.3 138 | ret = rapidjson.dumps(jsonobj) 139 | self.assertEqual(ret, "-12.3") 140 | 141 | def test_string(self): 142 | jsonobj = "hello world" 143 | ret = rapidjson.dumps(jsonobj) 144 | self.assertEqual(ret, "\"hello world\"") 145 | 146 | def test_true(self): 147 | jsonobj = True 148 | ret = rapidjson.dumps(jsonobj) 149 | self.assertEqual(ret, "true") 150 | 151 | def test_false(self): 152 | jsonobj = False 153 | ret = rapidjson.dumps(jsonobj) 154 | self.assertEqual(ret, "false") 155 | 156 | def test_none(self): 157 | jsonobj = None 158 | ret = rapidjson.dumps(jsonobj) 159 | self.assertEqual(ret, "null") 160 | 161 | def test_list_size_one(self): 162 | jsonobj = [None, ] 163 | ret = rapidjson.dumps(jsonobj) 164 | self.assertEqual(ret, "[null]") 165 | 166 | def test_tuple_size_one(self): 167 | jsonobj = (None, ) 168 | ret = rapidjson.dumps(jsonobj) 169 | self.assertEqual(ret, "[null]") 170 | 171 | def test_list_size_two(self): 172 | jsonobj = [False, -50.3] 173 | ret = rapidjson.dumps(jsonobj) 174 | self.assertEqual(ret, "[false,-50.3]") 175 | 176 | def test_dict_size_one(self): 177 | jsonobj = {'20': None} 178 | ret = rapidjson.dumps(jsonobj) 179 | self.assertEqual(ret, """{"20":null}""") 180 | 181 | def test_dict_size_two(self): 182 | jsonobj = {"hoge": None, "huga": 134} 183 | ret = rapidjson.dumps(jsonobj) 184 | self.assertEqual(""""hoge":null""" in ret, True) 185 | self.assertEqual(""""huga":134""" in ret, True) 186 | 187 | def test_dict_not_string_key(self): 188 | invalid_jsonobj = {1: 1} 189 | ret = rapidjson.dumps(invalid_jsonobj) 190 | self.assertEqual(ret, """{"1":1}""") 191 | 192 | def test_dict_not_string_key_long(self): 193 | invalid_jsonobj = {429496729501234567: 1} 194 | ret = rapidjson.dumps(invalid_jsonobj) 195 | self.assertEqual(ret, """{"429496729501234567":1}""") 196 | 197 | def test_dict_not_string_key_complex(self): 198 | invalid_jsonobj = {-1.99: 1} 199 | ret = rapidjson.dumps(invalid_jsonobj) 200 | self.assertEqual(ret, """{"-1.99":1}""") 201 | 202 | 203 | class TestEncodeComplex(unittest.TestCase): 204 | 205 | def test_list_in_list(self): 206 | jsonobj = [-123, [1, "hello"]] 207 | ret = rapidjson.dumps(jsonobj) 208 | self.assertEqual(ret, """[-123,[1,"hello"]]""") 209 | 210 | def test_list_in_dict(self): 211 | jsonobj = {"test": [1, "hello"]} 212 | ret = rapidjson.dumps(jsonobj) 213 | self.assertEqual(ret, """{"test":[1,"hello"]}""") 214 | 215 | def test_dict_in_dict(self): 216 | jsonobj = {"test": {"hello": "world"}} 217 | ret = rapidjson.dumps(jsonobj) 218 | self.assertEqual(ret, """{"test":{"hello":"world"}}""") 219 | 220 | def test_dict_in_dict_and_list(self): 221 | jsonobj = {"test": {"hello": ["world", "!!"]}} 222 | ret = rapidjson.dumps(jsonobj) 223 | self.assertEqual(ret, """{"test":{"hello":["world","!!"]}}""") 224 | 225 | def test_tuple_in_dict(self): 226 | jsonobj = {"test": (1, "hello")} 227 | ret = rapidjson.dumps(jsonobj) 228 | self.assertEqual(ret, """{"test":[1,"hello"]}""") 229 | 230 | 231 | class TestFileStream(unittest.TestCase): 232 | 233 | def test_dump(self): 234 | jsonobj = {"test": [1, "hello"]} 235 | fp = NamedTemporaryFile(mode='w', delete=False) 236 | rapidjson.dump(jsonobj, fp) 237 | fp.close() 238 | check_fp = open(fp.name) 239 | ret = json.load(check_fp) 240 | self.assertEqual(jsonobj, ret) 241 | check_fp.close() 242 | os.remove(fp.name) 243 | 244 | def test_dump_with_utf8(self): 245 | jsonobj = {"test": [1, "こんにちは"]} 246 | fp = NamedTemporaryFile(mode='w', delete=False) 247 | rapidjson.dump(jsonobj, fp) 248 | fp.close() 249 | check_fp = open(fp.name) 250 | ret = json.load(check_fp) 251 | self.assertEqual(jsonobj[u"test"][0], ret[u"test"][0]) 252 | check_fp.close() 253 | os.remove(fp.name) 254 | 255 | def test_dump_with_unicode(self): 256 | jsonobj = {"test": [1, u"こんにちは"]} 257 | fp = NamedTemporaryFile(mode='w', delete=False) 258 | rapidjson.dump(jsonobj, fp) 259 | fp.close() 260 | check_fp = open(fp.name) 261 | ret = json.load(check_fp) 262 | self.assertEqual(jsonobj[u"test"][0], ret[u"test"][0]) 263 | check_fp.close() 264 | os.remove(fp.name) 265 | 266 | def test_dump_with_invalid_fp(self): 267 | jsonobj = {"test": [1, "hello"]} 268 | fp = NamedTemporaryFile(mode='w', delete=False) 269 | fp.close() 270 | self.assertRaises(ValueError, rapidjson.dump, jsonobj, fp) 271 | os.remove(fp.name) 272 | 273 | def test_dump_with_io_stringio(self): 274 | jsonobj = {"test": [1, "hello"]} 275 | stream = StringIO() 276 | rapidjson.dump(jsonobj, stream) 277 | stream.seek(0) 278 | self.assertEqual("{\"test\":[1,\"hello\"]}", stream.read()) 279 | 280 | def test_dump_with_invalid_arg(self): 281 | jsonobj = {"test": [1, "hello"]} 282 | self.assertRaises(TypeError, rapidjson.dump, jsonobj, "") 283 | 284 | def test_load(self): 285 | jsonstr = b"""{"test": [1, "hello"]}""" 286 | fp = NamedTemporaryFile(delete=False) 287 | fp.write(jsonstr) 288 | fp.close() 289 | check_fp = open(fp.name) 290 | retobj = rapidjson.load(check_fp) 291 | self.assertEqual(retobj["test"], [1, "hello"]) 292 | # teardown 293 | check_fp.close() 294 | os.remove(fp.name) 295 | 296 | def test_load_simple(self): 297 | jsonstr = """1""" 298 | stream = StringIO() 299 | stream.write(jsonstr) 300 | stream.seek(0) 301 | retobj = rapidjson.load(stream) 302 | self.assertEqual(retobj, 1) 303 | 304 | def test_load_with_utf8(self): 305 | jsonstr = """{"test": [1, "こんにちは"]}""" 306 | if sys.version_info >= (3, ): 307 | fp = NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') 308 | else: 309 | fp = NamedTemporaryFile(mode='w', delete=False) 310 | fp.write(jsonstr) 311 | fp.close() 312 | check_fp = open(fp.name) 313 | retobj = rapidjson.load(check_fp) 314 | if sys.version_info >= (3, ): 315 | self.assertEqual(retobj["test"], [1, "こんにちは"]) 316 | else: 317 | self.assertEqual(retobj["test"], [1, u"こんにちは"]) 318 | # teardown 319 | check_fp.close() 320 | os.remove(fp.name) 321 | 322 | def test_load_with_io_stringio(self): 323 | jsonstr = """{"test": [1, "hello"]}""" 324 | stream = StringIO() 325 | stream.write(jsonstr) 326 | stream.seek(0) 327 | retobj = rapidjson.load(stream) 328 | self.assertEqual(retobj["test"], [1, "hello"]) 329 | 330 | def test_load_with_invalid_fp(self): 331 | jsonstr = b"""{"test": [1, "hello"]}""" 332 | fp = NamedTemporaryFile(delete=False) 333 | fp.write(jsonstr) 334 | fp.close() 335 | check_fp = open(fp.name) 336 | check_fp.close() 337 | self.assertRaises(ValueError, rapidjson.load, check_fp) 338 | # teardown 339 | os.remove(fp.name) 340 | 341 | def test_load_with_invalid_arg(self): 342 | self.assertRaises(TypeError, rapidjson.load, "") 343 | -------------------------------------------------------------------------------- /test/testall.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import sys 4 | import unittest 5 | 6 | 7 | test_root = os.path.dirname(os.path.abspath(__file__)) 8 | test_files = glob.glob(os.path.join(test_root, 'test_*.py')) 9 | 10 | os.chdir(test_root) 11 | sys.path.insert(0, os.path.dirname(test_root)) 12 | sys.path.insert(0, test_root) 13 | test_names = [os.path.basename(name)[:-3] for name in test_files] 14 | 15 | if __name__ == '__main__': 16 | suite = unittest.defaultTestLoader.loadTestsFromNames(test_names) 17 | ret = unittest.TextTestRunner().run(suite) 18 | retcode = 0 19 | if ret.failures or ret.errors: 20 | retcode = 1 21 | sys.exit(retcode) 22 | --------------------------------------------------------------------------------