#include #include "structmember.h" #include #include #include "libs.h" XPL_BEGIN_C_LINKAGE static PyMemberDef JsonObject_members[] = { { .name = NULL } }; static PyMethodDef JsonObject_methods[] = { { .ml_name = NULL } }; static PyMappingMethods JsonObject_mapping = { .mp_length = JsonObject_length, .mp_subscript = JsonObject_subscript, .mp_ass_subscript = JsonObject_subscript_assign }; PyTypeObject JsonObjectType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "libs.JsonObject", .tp_basicsize = sizeof(JsonObject), .tp_dealloc = (destructor)JsonObject_dealloc, .tp_as_mapping = &JsonObject_mapping, .tp_str = JsonObject_str, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = "Bongo Json Object", .tp_methods = JsonObject_methods, .tp_members = JsonObject_members, .tp_init = (initproc)JsonObject_init, .tp_new = JsonObject_new, }; static PyMemberDef JsonArray_members[] = { { .name = NULL } }; static PyMethodDef JsonArray_methods[] = { {"append", JsonArray_append, METH_VARARGS, "Add a json object"}, { .ml_name = NULL } }; static PySequenceMethods JsonArray_sequence = { .sq_length = JsonArray_length, .sq_item = JsonArray_item, }; #if 0 lenfunc sq_length; binaryfunc sq_concat; ssizeargfunc sq_repeat; ssizeargfunc sq_item; ssizessizeargfunc sq_slice; ssizeobjargproc sq_ass_item; ssizessizeobjargproc sq_ass_slice; objobjproc sq_contains; /* Added in release 2.0 */ binaryfunc sq_inplace_concat; ssizeargfunc sq_inplace_repeat; #endif PyTypeObject JsonArrayType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "libs.JsonArray", .tp_basicsize = sizeof(JsonArray), .tp_dealloc = (destructor)JsonArray_dealloc, .tp_as_sequence = &JsonArray_sequence, .tp_str = JsonArray_str, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = "Bongo Json Array", .tp_methods = JsonArray_methods, .tp_members = JsonArray_members, .tp_init = (initproc)JsonArray_init, .tp_new = JsonArray_new, }; static void RaiseJsonError(BongoJsonResult res) { PyErr_Format(PyExc_ValueError, "Error parsing json: %d", res); } static BongoJsonNode * JsonPyToNode(PyObject *obj) { BongoJsonNode *node; if (obj == Py_None) { return BongoJsonNodeNewNull(); } else if (PyBool_Check(obj)) { if (obj == Py_False) { node = BongoJsonNodeNewBool(FALSE); } else { node = BongoJsonNodeNewBool(TRUE); } } else if (PyInt_Check(obj)) { node = BongoJsonNodeNewInt(PyInt_AsLong(obj)); } else if (PyLong_Check(obj)) { node = BongoJsonNodeNewInt(PyLong_AsLong(obj)); } else if (PyFloat_Check(obj)) { node = BongoJsonNodeNewDouble(PyFloat_AsDouble(obj)); } else if (PyString_Check(obj)) { node = BongoJsonNodeNewString(PyString_AsString(obj)); } else if (PyUnicode_Check(obj)) { PyObject *utf8 = PyUnicode_AsUTF8String(obj); node = BongoJsonNodeNewString(PyString_AsString(utf8)); Py_DECREF(utf8); } else if (PyList_Check(obj)) { GArray *array; int len = PyList_Size(obj); int i; array = g_array_sized_new(FALSE, FALSE, sizeof(BongoJsonNode*), len); for (i=0; iobj); json->own = FALSE; } else if (PyObject_IsInstance(obj, (PyObject*)&JsonArrayType)) { JsonArray *json = ((JsonArray*)obj); node = BongoJsonNodeNewArray(json->array); json->own = FALSE; } else { node = NULL; } if (node == NULL) { PyErr_SetString(PyExc_TypeError, "Creating JSON node from unsupported type"); } return node; } PyObject * JsonObjectToPy(BongoJsonObject *object, BOOL own) { PyObject *ret = (PyObject*)PyObject_New(JsonObject, &JsonObjectType); ((JsonObject*)ret)->obj = object; ((JsonObject*)ret)->own = own; ((JsonObject*)ret)->valid = TRUE; return ret; } static PyObject * JsonObjectToPyNative(BongoJsonObject *obj) { PyObject *ret; ret = PyDict_New(); BongoJsonObjectIter iter; for (BongoJsonObjectIterFirst(obj, &iter); iter.key != NULL; BongoJsonObjectIterNext(obj, &iter)) { PyObject *child; child = JsonNodeToPy(iter.value, FALSE, TRUE); if (!child) { Py_DECREF(ret); return NULL; } if (PyDict_SetItemString (ret, iter.key, child) == -1) { Py_DECREF(ret); return NULL; } } return ret; } static PyObject * JsonArrayToPyNative(GArray *array) { PyObject *ret; unsigned int i; ret = PyList_New(0); for (i = 0; i < array->len; i++) { PyObject *child; child = JsonNodeToPy (BongoJsonArrayGet(array, i), FALSE, TRUE); if (!child) { Py_DECREF(ret); return NULL; } if (PyList_Append (ret, child) == -1) { Py_DECREF(ret); return NULL; } } return ret; } PyObject * JsonNodeToPy(BongoJsonNode *node, BOOL own, BOOL native) { PyObject *ret; char *str; switch(node->type) { case BONGO_JSON_OBJECT : if (native) { return JsonObjectToPyNative(BongoJsonNodeAsObject(node)); } else { ret = JsonObjectToPy(BongoJsonNodeAsObject(node), own); } break; case BONGO_JSON_ARRAY : if (native) { return JsonArrayToPyNative(BongoJsonNodeAsArray (node)); } else { ret = (PyObject*)PyObject_New(JsonArray, &JsonArrayType); ((JsonArray*)ret)->own = own; ((JsonArray*)ret)->array = BongoJsonNodeAsArray(node); } break; case BONGO_JSON_BOOL : if (BongoJsonNodeAsBool(node)) { ret = Py_True; } else { ret = Py_False; } Py_INCREF(ret); break; case BONGO_JSON_DOUBLE : ret = Py_BuildValue("d", BongoJsonNodeAsDouble(node)); break; case BONGO_JSON_INT : ret = PyLong_FromLongLong(BongoJsonNodeAsInt(node)); break; case BONGO_JSON_STRING : str = BongoJsonNodeAsString(node); ret = PyUnicode_DecodeUTF8(str, strlen(str), NULL); break; default : PyErr_Format(PyExc_ValueError, "Unknown node type: %d", node->type); ret = NULL; } return ret; } /*** JsonArray ***/ void JsonArray_dealloc(JsonArray *self) { if (self->own) { BongoJsonArrayFree(self->array); } Py_TYPE(self)->tp_free((PyObject*)self); } PyObject * JsonArray_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { JsonArray *self; (void)args; (void)kwds; self = (JsonArray*)type->tp_alloc(type, 0); self->own = FALSE; self->array = NULL; /* JsonArray is defined as a c subclass of the PyObject. * i should be able to cast here safely because i'm casting * a pointer either way */ return (PyObject *)self; } int JsonArray_init(JsonArray *self, PyObject *args, PyObject *kwds) { (void)args; (void)kwds; self->own = TRUE; self->array = g_array_sized_new(FALSE, FALSE, sizeof(BongoJsonNode*), 16); return 0; } PyObject * JsonArray_append(PyObject *selfp, PyObject *args) { JsonArray *self = (JsonArray*)selfp; PyObject *obj; BongoJsonNode *node; if (!PyArg_ParseTuple(args, "O", &obj)) { PyErr_SetString(PyExc_TypeError, "append() takes 1 argument"); return NULL; } node = JsonPyToNode(obj); if (!node) { return NULL; } BongoJsonArrayAppend(self->array, node); Py_INCREF(Py_None); return Py_None; } PyObject * JsonArray_str(PyObject *selfp) { JsonArray *self = (JsonArray *)selfp; PyObject *ret; char *str; str = BongoJsonArrayToString(self->array); ret = Py_BuildValue("s", str); MemFree(str); return ret; } Py_ssize_t JsonArray_length(PyObject *selfp) { JsonArray *self = (JsonArray *)selfp; return (self->array->len > 0) ? (Py_ssize_t)self->array->len : 0; } PyObject * JsonArray_item(PyObject *selfp, Py_ssize_t i) { JsonArray *self = (JsonArray *)selfp; BongoJsonNode *node; if (i < 0 || i >= self->array->len) { PyErr_SetString(PyExc_IndexError, "Out of bounds"); return NULL; } node = BongoJsonArrayGet(self->array, i); return JsonNodeToPy(node, FALSE, FALSE); } /*** JsonObject ***/ void JsonObject_dealloc(JsonObject *self) { #if 0 if (self->obj) { BongoJsonObjectFree(self->obj); } #endif Py_TYPE(self)->tp_free((PyObject*)self); } PyObject * JsonObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { JsonObject *self; (void)args; (void)kwds; self = (JsonObject*)type->tp_alloc(type, 0); self->obj = NULL; self->own = FALSE; self->valid = TRUE; /* JsonObject is defined as a c subclass of the PyObject. * i should be able to cast here safely because i'm casting * a pointer either way */ return (PyObject *)self; } int JsonObject_init(JsonObject *self, PyObject *args, PyObject *kwds) { (void)args; (void)kwds; self->obj = BongoJsonObjectNew(); self->own = TRUE; self->valid = TRUE; return 0; } PyObject * JsonObject_str(PyObject *selfp) { JsonObject *self = (JsonObject *)selfp; PyObject *ret; char *str; str = BongoJsonObjectToString(self->obj); ret = Py_BuildValue("s", str); MemFree(str); return ret; } Py_ssize_t JsonObject_length(PyObject *self) { (void)self; /* FIXME */ return 1; } PyObject * JsonObject_subscript(PyObject *selfp, PyObject *key) { JsonObject *self = (JsonObject*)selfp; char *str; BongoJsonNode *node; BongoJsonResult res; if (!PyString_Check(key)) { PyErr_SetString(PyExc_KeyError, "Key must be a string"); return NULL; } str = PyString_AsString(key); res = BongoJsonObjectGet(self->obj, str, &node); if (res != BONGO_JSON_OK) { RaiseJsonError(res); return NULL; } return JsonNodeToPy(node, FALSE, FALSE); } int JsonObject_subscript_assign(PyObject *selfp, PyObject *key, PyObject *value) { JsonObject *self = (JsonObject*)selfp; BongoJsonNode *node; BongoJsonResult res; node = JsonPyToNode(value); if (!node) { return 0; } if (!PyString_Check(key)) { PyErr_SetString(PyExc_KeyError, "Key must be a string"); return 0; } res = BongoJsonObjectPut(self->obj, PyString_AsString(key), node); if (res != BONGO_JSON_OK) { RaiseJsonError(res); return 0; } return 0; } /* Static */ BOOL BongoJsonPreInit(void) { JsonObjectType.tp_new = PyType_GenericNew; if (PyType_Ready(&JsonObjectType) < 0) { return FALSE; } JsonArrayType.tp_new = PyType_GenericNew; if (PyType_Ready(&JsonArrayType) < 0) { return FALSE; } return TRUE; } BOOL BongoJsonPostInit(PyObject *module) { Py_INCREF(&JsonObjectType); PyModule_AddObject(module, "JsonObject", (PyObject *)&JsonObjectType); Py_INCREF(&JsonArrayType); PyModule_AddObject(module, "JsonArray", (PyObject *)&JsonArrayType); return TRUE; } static PyObject * bongojson_BongoJsonParseString(PyObject *self, PyObject *args) { (void)self; char *jsonStr; BongoJsonNode *node; BongoJsonResult res; PyObject *ret; PyObject *obj; PyObject *strObj = NULL; if (!PyArg_ParseTuple(args, "O", &obj)) { PyErr_SetString(PyExc_TypeError, "ParseString takes one argument"); return NULL; } if (PyString_Check(obj)) { jsonStr = PyString_AsString(obj); } else if (PyUnicode_Check(obj)) { strObj = PyUnicode_AsUTF8String(obj); jsonStr = PyString_AsString(strObj); } else { PyErr_SetString(PyExc_TypeError, "Argument 1 must be a string or unicode object"); return NULL; } res = BongoJsonParseString(jsonStr, &node); if (strObj) { Py_DECREF(strObj); } if (res != BONGO_JSON_OK) { RaiseJsonError(res); return NULL; } /* Pull out the value and throw away the node */ ret = JsonNodeToPy(node, FALSE, FALSE); if (node->type == BONGO_JSON_STRING) { BongoJsonNodeFree(node); } else { BongoJsonNodeFreeSteal(node); } return ret; } static PyObject * bongojson_ToPy(PyObject *self, PyObject *args) { (void)self; PyObject *obj; PyObject *ret; if (!PyArg_ParseTuple(args, "O", &obj)) { PyErr_SetString(PyExc_TypeError, "ToPy takes one argument"); return NULL; } if (PyObject_IsInstance(obj, (PyObject*)&JsonObjectType)) { JsonObject *json = ((JsonObject*)obj); ret = JsonObjectToPyNative(json->obj); if (!ret) { PyErr_SetString(PyExc_RuntimeError, "Couldn't convert json to python"); return NULL; } return ret; } else if (PyObject_IsInstance(obj, (PyObject*)&JsonArrayType)) { JsonArray *json = ((JsonArray*)obj); ret = JsonArrayToPyNative(json->array); if (!ret) { PyErr_SetString(PyExc_RuntimeError, "Couldn't convert json to python"); return NULL; } return ret; } else { PyErr_SetString(PyExc_TypeError, "Argument 1 must be a bongojson object or array"); return NULL; } } PyMethodDef BongoJsonMethods[] = { {"ParseString", bongojson_BongoJsonParseString, METH_VARARGS | METH_STATIC, "Parse a json string"}, {"ToPy", bongojson_ToPy, METH_VARARGS | METH_STATIC, "Convert a json object to a native python object"}, {NULL, NULL, 0, NULL} }; XPL_END_C_LINKAGE