// -*- coding: utf-8 -*-
// :Project: python-rapidjson -- Python extension module
// :Author: Ken Robbins
// :License: MIT License
// :Copyright: 2015 Ken Robbins
// :Copyright: 2015-2026 Lele Gaifax
//
#include
#include
#include
#include
#include
#include
#include
#include
#include "rapidjson/reader.h"
#include "rapidjson/schema.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/error/en.h"
using namespace rapidjson;
/* On some MacOS combo, using Py_IS_XXX() macros does not work (see
https://github.com/python-rapidjson/python-rapidjson/issues/78).
OTOH, MSVC < 2015 does not have std::isxxx() (see
https://stackoverflow.com/questions/38441740/where-is-isnan-in-msvc-2010).
Oh well... */
#if defined (_MSC_VER) && (_MSC_VER < 1900)
#define IS_NAN(x) Py_IS_NAN(x)
#define IS_INF(x) Py_IS_INFINITY(x)
#else
#define IS_NAN(x) std::isnan(x)
#define IS_INF(x) std::isinf(x)
#endif
static PyObject* decimal_type = NULL;
static PyObject* timezone_type = NULL;
static PyObject* timezone_utc = NULL;
static PyObject* uuid_type = NULL;
static PyObject* validation_error = NULL;
static PyObject* decode_error = NULL;
/* These are the names of often used methods or literal values, interned in the module
initialization function, to avoid repeated creation/destruction of PyUnicode values
from plain C strings.
We cannot use _Py_IDENTIFIER() because that upsets the GNU C++ compiler in -pedantic
mode. */
static PyObject* astimezone_name = NULL;
static PyObject* hex_name = NULL;
static PyObject* timestamp_name = NULL;
static PyObject* total_seconds_name = NULL;
static PyObject* utcoffset_name = NULL;
static PyObject* is_infinite_name = NULL;
static PyObject* is_nan_name = NULL;
static PyObject* start_object_name = NULL;
static PyObject* end_object_name = NULL;
static PyObject* default_name = NULL;
static PyObject* end_array_name = NULL;
static PyObject* string_name = NULL;
static PyObject* read_name = NULL;
static PyObject* write_name = NULL;
static PyObject* encoding_name = NULL;
static PyObject* minus_inf_string_value = NULL;
static PyObject* nan_string_value = NULL;
static PyObject* plus_inf_string_value = NULL;
struct HandlerContext {
PyObject* object;
const char* key;
SizeType keyLength;
bool isObject;
bool keyValuePairs;
bool copiedKey;
};
enum DatetimeMode {
DM_NONE = 0,
// Formats
DM_ISO8601 = 1 UINT_MAX) {
PyErr_SetString(PyExc_ValueError,
"Invalid chunk_size, must be an integer between 4 and"
" UINT_MAX");
return NULL;
}
chunkSize = (size_t) size;
} else {
PyErr_SetString(PyExc_TypeError,
"chunk_size must be an unsigned integer value or None");
return NULL;
}
}
Py_ssize_t jsonStrLen;
const char* jsonStr;
PyObject* asUnicode = NULL;
if (PyUnicode_Check(jsonObject)) {
jsonStr = PyUnicode_AsUTF8AndSize(jsonObject, &jsonStrLen);
if (jsonStr == NULL)
return NULL;
} else if (PyBytes_Check(jsonObject) || PyByteArray_Check(jsonObject)) {
asUnicode = PyUnicode_FromEncodedObject(jsonObject, "utf-8", NULL);
if (asUnicode == NULL)
return NULL;
jsonStr = PyUnicode_AsUTF8AndSize(asUnicode, &jsonStrLen);
if (jsonStr == NULL) {
Py_DECREF(asUnicode);
return NULL;
}
} else if (PyObject_HasAttr(jsonObject, read_name)) {
jsonStr = NULL;
jsonStrLen = 0;
} else {
PyErr_SetString(
PyExc_TypeError,
"Expected string or UTF-8 encoded bytes or bytearray or a file-like object");
return NULL;
}
DecoderObject* d = (DecoderObject*) self;
PyObject* result = do_decode(self, jsonStr, jsonStrLen, jsonObject, chunkSize, NULL,
d->numberMode, d->datetimeMode, d->uuidMode,
d->parseMode);
if (asUnicode != NULL)
Py_DECREF(asUnicode);
return result;
}
static PyObject*
decoder_new(PyTypeObject* type, PyObject* args, PyObject* kwargs)
{
DecoderObject* d;
PyObject* datetimeModeObj = NULL;
unsigned datetimeMode = DM_NONE;
PyObject* uuidModeObj = NULL;
unsigned uuidMode = UM_NONE;
PyObject* numberModeObj = NULL;
unsigned numberMode = NM_NAN;
PyObject* parseModeObj = NULL;
unsigned parseMode = PM_NONE;
static char const* kwlist[] = {
"number_mode",
"datetime_mode",
"uuid_mode",
"parse_mode",
NULL
};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO:Decoder",
(char**) kwlist,
&numberModeObj,
&datetimeModeObj,
&uuidModeObj,
&parseModeObj))
return NULL;
if (numberModeObj) {
if (numberModeObj == Py_None) {
numberMode = NM_NONE;
} else if (PyLong_Check(numberModeObj)) {
int mode = PyLong_AsLong(numberModeObj);
if (mode < 0 || mode >= NM_MAX) {
PyErr_SetString(PyExc_ValueError, "Invalid number_mode");
return NULL;
}
numberMode = (unsigned) mode;
if (numberMode & NM_DECIMAL && numberMode & NM_NATIVE) {
PyErr_SetString(PyExc_ValueError,
"Combining NM_NATIVE with NM_DECIMAL is not supported");
return NULL;
}
}
}
if (datetimeModeObj) {
if (datetimeModeObj == Py_None) {
datetimeMode = DM_NONE;
} else if (PyLong_Check(datetimeModeObj)) {
int mode = PyLong_AsLong(datetimeModeObj);
if (!valid_datetime_mode(mode)) {
PyErr_SetString(PyExc_ValueError, "Invalid datetime_mode");
return NULL;
}
datetimeMode = (unsigned) mode;
if (datetimeMode && datetime_mode_format(datetimeMode) != DM_ISO8601) {
PyErr_SetString(PyExc_ValueError,
"Invalid datetime_mode, can deserialize only from"
" ISO8601");
return NULL;
}
} else {
PyErr_SetString(PyExc_TypeError,
"datetime_mode must be a non-negative integer value or None");
return NULL;
}
}
if (uuidModeObj) {
if (uuidModeObj == Py_None) {
uuidMode = UM_NONE;
} else if (PyLong_Check(uuidModeObj)) {
int mode = PyLong_AsLong(uuidModeObj);
if (mode < 0 || mode >= UM_MAX) {
PyErr_SetString(PyExc_ValueError, "Invalid uuid_mode");
return NULL;
}
uuidMode = (unsigned) mode;
} else {
PyErr_SetString(PyExc_TypeError,
"uuid_mode must be an integer value or None");
return NULL;
}
}
if (parseModeObj) {
if (parseModeObj == Py_None) {
parseMode = PM_NONE;
} else if (PyLong_Check(parseModeObj)) {
int mode = PyLong_AsLong(parseModeObj);
if (mode < 0 || mode >= PM_MAX) {
PyErr_SetString(PyExc_ValueError, "Invalid parse_mode");
return NULL;
}
parseMode = (unsigned) mode;
} else {
PyErr_SetString(PyExc_TypeError,
"parse_mode must be an integer value or None");
return NULL;
}
}
d = (DecoderObject*) type->tp_alloc(type, 0);
if (d == NULL)
return NULL;
d->datetimeMode = datetimeMode;
d->uuidMode = uuidMode;
d->numberMode = numberMode;
d->parseMode = parseMode;
return (PyObject*) d;
}
/////////////
// Encoder //
/////////////
struct DictItem {
std::string key;
PyObject* value;
DictItem(std::string k,
PyObject* v)
: key(k),
value(v)
{}
bool operator UINT_MAX) { \
PyErr_SetString(PyExc_ValueError, "Out of range string size"); \
return false; \
} } while(0)
if (object == Py_None) {
writer->Null();
} else if (PyBool_Check(object)) {
writer->Bool(object == Py_True);
} else if (numberMode & NM_DECIMAL
&& (is_decimal = PyObject_IsInstance(object, decimal_type))) {
if (is_decimal == -1) {
return false;
}
if (!(numberMode & NM_NAN)) {
bool is_inf_or_nan;
PyObject* is_inf = PyObject_CallMethodObjArgs(object, is_infinite_name,
NULL);
if (is_inf == NULL) {
return false;
}
is_inf_or_nan = is_inf == Py_True;
Py_DECREF(is_inf);
if (!is_inf_or_nan) {
PyObject* is_nan = PyObject_CallMethodObjArgs(object, is_nan_name,
NULL);
if (is_nan == NULL) {
return false;
}
is_inf_or_nan = is_nan == Py_True;
Py_DECREF(is_nan);
}
if (is_inf_or_nan) {
PyErr_SetString(PyExc_ValueError,
"Out of range decimal values are not JSON compliant");
return false;
}
}
PyObject* decStrObj = PyObject_Str(object);
if (decStrObj == NULL)
return false;
Py_ssize_t size;
const char* decStr = PyUnicode_AsUTF8AndSize(decStrObj, &size);
if (decStr == NULL) {
Py_DECREF(decStrObj);
return false;
}
writer->RawValue(decStr, size, kNumberType);
Py_DECREF(decStrObj);
} else if (PyLong_Check(object)) {
if (numberMode & NM_NATIVE) {
int overflow;
long long i = PyLong_AsLongLongAndOverflow(object, &overflow);
if (i == -1 && PyErr_Occurred())
return false;
if (overflow == 0) {
writer->Int64(i);
} else {
unsigned long long ui = PyLong_AsUnsignedLongLong(object);
if (PyErr_Occurred())
return false;
writer->Uint64(ui);
}
} else {
// Mimic stdlib json: subclasses of int may override __repr__, but we still
// want to encode them as integers in JSON; one example within the standard
// library is IntEnum
PyObject* intStrObj = PyLong_Type.tp_repr(object);
if (intStrObj == NULL)
return false;
Py_ssize_t size;
const char* intStr = PyUnicode_AsUTF8AndSize(intStrObj, &size);
if (intStr == NULL) {
Py_DECREF(intStrObj);
return false;
}
writer->RawValue(intStr, size, kNumberType);
Py_DECREF(intStrObj);
}
} else if (PyFloat_Check(object)) {
double d = PyFloat_AS_DOUBLE(object);
if (IS_NAN(d)) {
if (numberMode & NM_NAN) {
writer->RawValue("NaN", 3, kNumberType);
} else {
PyErr_SetString(PyExc_ValueError,
"Out of range float values are not JSON compliant");
return false;
}
} else if (IS_INF(d)) {
if (!(numberMode & NM_NAN)) {
PyErr_SetString(PyExc_ValueError,
"Out of range float values are not JSON compliant");
return false;
} else if (d < 0) {
writer->RawValue("-Infinity", 9, kNumberType);
} else {
writer->RawValue("Infinity", 8, kNumberType);
}
} else {
// The RJ dtoa() produces "strange" results for particular values, see #101:
// use Python's repr() to emit a raw value instead of writer->Double(d)
PyObject* dr = PyFloat_Type.tp_repr(object);
if (dr == NULL)
return false;
Py_ssize_t l;
const char* rs = PyUnicode_AsUTF8AndSize(dr, &l);
if (rs == NULL) {
Py_DECREF(dr);
return false;
}
writer->RawValue(rs, l, kNumberType);
Py_DECREF(dr);
}
} else if (PyUnicode_Check(object)) {
Py_ssize_t l;
const char* s = PyUnicode_AsUTF8AndSize(object, &l);
if (s == NULL)
return false;
ASSERT_VALID_SIZE(l);
writer->String(s, (SizeType) l);
} else if (bytesMode == BM_UTF8
&& (PyBytes_Check(object) || PyByteArray_Check(object))) {
PyObject* unicodeObj = PyUnicode_FromEncodedObject(object, "utf-8", NULL);
if (unicodeObj == NULL)
return false;
Py_ssize_t l;
const char* s = PyUnicode_AsUTF8AndSize(unicodeObj, &l);
if (s == NULL) {
Py_DECREF(unicodeObj);
return false;
}
ASSERT_VALID_SIZE(l);
writer->String(s, (SizeType) l);
Py_DECREF(unicodeObj);
} else if (PyList_CheckExact(object)
||
(!(iterableMode & IM_ONLY_LISTS) && PyList_Check(object))) {
writer->StartArray();
Py_ssize_t size = PyList_GET_SIZE(object);
for (Py_ssize_t i = 0; i < size; i++) {
if (Py_EnterRecursiveCall(" while JSONifying list object"))
return false;
PyObject* item = PyList_GET_ITEM(object, i);
bool r = RECURSE(item);
Py_LeaveRecursiveCall();
if (!r)
return false;
}
writer->EndArray();
} else if (!(iterableMode & IM_ONLY_LISTS) && PyTuple_Check(object)) {
writer->StartArray();
Py_ssize_t size = PyTuple_GET_SIZE(object);
for (Py_ssize_t i = 0; i < size; i++) {
if (Py_EnterRecursiveCall(" while JSONifying tuple object"))
return false;
PyObject* item = PyTuple_GET_ITEM(object, i);
bool r = RECURSE(item);
Py_LeaveRecursiveCall();
if (!r)
return false;
}
writer->EndArray();
} else if ((PyDict_CheckExact(object)
||
(!(mappingMode & MM_ONLY_DICTS) && PyDict_Check(object)))
&&
((mappingMode & MM_SKIP_NON_STRING_KEYS)
||
(mappingMode & MM_COERCE_KEYS_TO_STRINGS)
||
all_keys_are_string(object))) {
writer->StartObject();
Py_ssize_t pos = 0;
PyObject* key;
PyObject* item;
PyObject* coercedKey = NULL;
if (!(mappingMode & MM_SORT_KEYS)) {
while (PyDict_Next(object, &pos, &key, &item)) {
if (mappingMode & MM_COERCE_KEYS_TO_STRINGS) {
if (!PyUnicode_Check(key)) {
coercedKey = PyObject_Str(key);
if (coercedKey == NULL)
return false;
key = coercedKey;
}
}
if (coercedKey || PyUnicode_Check(key)) {
Py_ssize_t l;
const char* key_str = PyUnicode_AsUTF8AndSize(key, &l);
if (key_str == NULL) {
Py_XDECREF(coercedKey);
return false;
}
ASSERT_VALID_SIZE(l);
writer->Key(key_str, (SizeType) l);
if (Py_EnterRecursiveCall(" while JSONifying dict object")) {
Py_XDECREF(coercedKey);
return false;
}
bool r = RECURSE(item);
Py_LeaveRecursiveCall();
if (!r) {
Py_XDECREF(coercedKey);
return false;
}
} else if (!(mappingMode & MM_SKIP_NON_STRING_KEYS)) {
PyErr_SetString(PyExc_TypeError, "keys must be strings");
// No need to dispose coercedKey here, because it can be set *only*
// when mapping_mode is MM_COERCE_KEYS_TO_STRINGS
assert(!coercedKey);
return false;
}
Py_CLEAR(coercedKey);
}
} else {
std::vector items;
while (PyDict_Next(object, &pos, &key, &item)) {
if (mappingMode & MM_COERCE_KEYS_TO_STRINGS) {
if (!PyUnicode_Check(key)) {
coercedKey = PyObject_Str(key);
if (coercedKey == NULL)
return false;
key = coercedKey;
}
}
if (coercedKey || PyUnicode_Check(key)) {
Py_ssize_t l;
const char* key_str = PyUnicode_AsUTF8AndSize(key, &l);
if (key_str == NULL) {
Py_XDECREF(coercedKey);
return false;
}
ASSERT_VALID_SIZE(l);
items.push_back(DictItem(std::string(key_str, l), item));
} else if (!(mappingMode & MM_SKIP_NON_STRING_KEYS)) {
PyErr_SetString(PyExc_TypeError, "keys must be strings");
assert(!coercedKey);
return false;
}
Py_CLEAR(coercedKey);
}
std::sort(items.begin(), items.end());
for (size_t i=0, s=items.size(); i < s; i++) {
writer->Key(items[i].key.c_str(), (SizeType) items[i].key.length());
if (Py_EnterRecursiveCall(" while JSONifying dict object"))
return false;
bool r = RECURSE(items[i].value);
Py_LeaveRecursiveCall();
if (!r)
return false;
}
}
writer->EndObject();
} else if (datetimeMode != DM_NONE
&& (PyTime_Check(object) || PyDateTime_Check(object))) {
unsigned year, month, day, hour, min, sec, microsec;
PyObject* dtObject = object;
PyObject* asUTC = NULL;
const int ISOFORMAT_LEN = 42;
char isoformat[ISOFORMAT_LEN];
memset(isoformat, 0, ISOFORMAT_LEN);
// The timezone is always shorter than this, but gcc12 emits a warning about
// sprintf() that *may* produce longer results, because we pass int values when
// concretely they are constrained to 24*3600 seconds: pacify gcc using a bigger
// buffer
const int TIMEZONE_LEN = 24;
char timeZone[TIMEZONE_LEN] = { 0 };
if (!(datetimeMode & DM_IGNORE_TZ)
&& PyObject_HasAttr(object, utcoffset_name)) {
PyObject* utcOffset = PyObject_CallMethodObjArgs(object,
utcoffset_name,
NULL);
if (utcOffset == NULL)
return false;
if (utcOffset == Py_None) {
// Naive value: maybe assume it's in UTC instead of local time
if (datetimeMode & DM_NAIVE_IS_UTC) {
if (PyDateTime_Check(object)) {
hour = PyDateTime_DATE_GET_HOUR(dtObject);
min = PyDateTime_DATE_GET_MINUTE(dtObject);
sec = PyDateTime_DATE_GET_SECOND(dtObject);
microsec = PyDateTime_DATE_GET_MICROSECOND(dtObject);
year = PyDateTime_GET_YEAR(dtObject);
month = PyDateTime_GET_MONTH(dtObject);
day = PyDateTime_GET_DAY(dtObject);
asUTC = PyDateTimeAPI->DateTime_FromDateAndTime(
year, month, day, hour, min, sec, microsec,
timezone_utc, PyDateTimeAPI->DateTimeType);
} else {
hour = PyDateTime_TIME_GET_HOUR(dtObject);
min = PyDateTime_TIME_GET_MINUTE(dtObject);
sec = PyDateTime_TIME_GET_SECOND(dtObject);
microsec = PyDateTime_TIME_GET_MICROSECOND(dtObject);
asUTC = PyDateTimeAPI->Time_FromTime(
hour, min, sec, microsec,
timezone_utc, PyDateTimeAPI->TimeType);
}
if (asUTC == NULL) {
Py_DECREF(utcOffset);
return false;
}
dtObject = asUTC;
if (datetime_mode_format(datetimeMode) == DM_ISO8601)
strcpy(timeZone, "+00:00");
}
} else {
// Timezone-aware value
if (datetimeMode & DM_SHIFT_TO_UTC) {
// If it's not already in UTC, shift the value
if (PyObject_IsTrue(utcOffset)) {
asUTC = PyObject_CallMethodObjArgs(object, astimezone_name,
timezone_utc, NULL);
if (asUTC == NULL) {
Py_DECREF(utcOffset);
return false;
}
dtObject = asUTC;
}
if (datetime_mode_format(datetimeMode) == DM_ISO8601)
strcpy(timeZone, "+00:00");
} else if (datetime_mode_format(datetimeMode) == DM_ISO8601) {
int seconds_from_utc = 0;
if (PyObject_IsTrue(utcOffset)) {
PyObject* tsObj = PyObject_CallMethodObjArgs(utcOffset,
total_seconds_name,
NULL);
if (tsObj == NULL) {
Py_DECREF(utcOffset);
return false;
}
seconds_from_utc = (int) PyFloat_AsDouble(tsObj);
Py_DECREF(tsObj);
}
char sign = '+';
if (seconds_from_utc < 0) {
sign = '-';
seconds_from_utc = -seconds_from_utc;
}
unsigned tz_hour = seconds_from_utc / 3600;
unsigned tz_min = (seconds_from_utc % 3600) / 60;
snprintf(timeZone, TIMEZONE_LEN-1, "%c%02u:%02u",
sign, tz_hour, tz_min);
}
}
Py_DECREF(utcOffset);
}
if (datetime_mode_format(datetimeMode) == DM_ISO8601) {
int size;
if (PyDateTime_Check(dtObject)) {
year = PyDateTime_GET_YEAR(dtObject);
month = PyDateTime_GET_MONTH(dtObject);
day = PyDateTime_GET_DAY(dtObject);
hour = PyDateTime_DATE_GET_HOUR(dtObject);
min = PyDateTime_DATE_GET_MINUTE(dtObject);
sec = PyDateTime_DATE_GET_SECOND(dtObject);
microsec = PyDateTime_DATE_GET_MICROSECOND(dtObject);
if (microsec > 0) {
size = snprintf(isoformat,
ISOFORMAT_LEN-1,
"\"%04u-%02u-%02uT%02u:%02u:%02u.%06u%s\"",
year, month, day,
hour, min, sec, microsec,
timeZone);
} else {
size = snprintf(isoformat,
ISOFORMAT_LEN-1,
"\"%04u-%02u-%02uT%02u:%02u:%02u%s\"",
year, month, day,
hour, min, sec,
timeZone);
}
} else {
hour = PyDateTime_TIME_GET_HOUR(dtObject);
min = PyDateTime_TIME_GET_MINUTE(dtObject);
sec = PyDateTime_TIME_GET_SECOND(dtObject);
microsec = PyDateTime_TIME_GET_MICROSECOND(dtObject);
if (microsec > 0) {
size = snprintf(isoformat,
ISOFORMAT_LEN-1,
"\"%02u:%02u:%02u.%06u%s\"",
hour, min, sec, microsec,
timeZone);
} else {
size = snprintf(isoformat,
ISOFORMAT_LEN-1,
"\"%02u:%02u:%02u%s\"",
hour, min, sec,
timeZone);
}
}
writer->RawValue(isoformat, size, kStringType);
} else /* if (datetimeMode & DM_UNIX_TIME) */ {
if (PyDateTime_Check(dtObject)) {
PyObject* timestampObj = PyObject_CallMethodObjArgs(dtObject,
timestamp_name,
NULL);
if (timestampObj == NULL) {
Py_XDECREF(asUTC);
return false;
}
double timestamp = PyFloat_AsDouble(timestampObj);
Py_DECREF(timestampObj);
if (datetimeMode & DM_ONLY_SECONDS) {
writer->Int64((int64_t) timestamp);
} else {
// Writer.SetMaxDecimalPlaces(6) truncates the value,
// so for example 1514893636.276703 would come out as
// 1514893636.276702, because its exact double value is
// 1514893636.2767028808593750000000000...
char tsStr[12 + 1 + 6 + 1];
// Temporarily switch to a POSIX locale, in case the outer world is
// configured differently: by chance I got one doctest failure, and I
// can only imagine that recent Sphinx (that is, 4.2+) initializes the
// locale to something that implies a decimal separator different from
// a dot ".", say a comma "," when LANG is "it_IT"... not the best
// thing to do when emitting JSON!
const char* locale = setlocale(LC_NUMERIC, NULL);
setlocale(LC_NUMERIC, "C");
int size = snprintf(tsStr, 12 + 1 + 6, "%.6f", timestamp);
setlocale(LC_NUMERIC, locale);
// Remove trailing 0s
while (tsStr[size-2] != '.' && tsStr[size-1] == '0')
size--;
writer->RawValue(tsStr, size, kNumberType);
}
} else {
hour = PyDateTime_TIME_GET_HOUR(dtObject);
min = PyDateTime_TIME_GET_MINUTE(dtObject);
sec = PyDateTime_TIME_GET_SECOND(dtObject);
microsec = PyDateTime_TIME_GET_MICROSECOND(dtObject);
long timestamp = hour * 3600 + min * 60 + sec;
if (datetimeMode & DM_ONLY_SECONDS)
writer->Int64(timestamp);
else
writer->Double(timestamp + (microsec / 1000000.0));
}
}
Py_XDECREF(asUTC);
} else if (datetimeMode != DM_NONE && PyDate_Check(object)) {
unsigned year = PyDateTime_GET_YEAR(object);
unsigned month = PyDateTime_GET_MONTH(object);
unsigned day = PyDateTime_GET_DAY(object);
if (datetime_mode_format(datetimeMode) == DM_ISO8601) {
const int ISOFORMAT_LEN = 18;
char isoformat[ISOFORMAT_LEN];
int size;
memset(isoformat, 0, ISOFORMAT_LEN);
size = snprintf(isoformat, ISOFORMAT_LEN-1, "\"%04u-%02u-%02u\"",
year, month, day);
writer->RawValue(isoformat, size, kStringType);
} else /* datetime_mode_format(datetimeMode) == DM_UNIX_TIME */ {
// A date object, take its midnight timestamp
PyObject* midnightObj;
PyObject* timestampObj;
if (datetimeMode & (DM_SHIFT_TO_UTC | DM_NAIVE_IS_UTC))
midnightObj = PyDateTimeAPI->DateTime_FromDateAndTime(
year, month, day, 0, 0, 0, 0,
timezone_utc, PyDateTimeAPI->DateTimeType);
else
midnightObj = PyDateTime_FromDateAndTime(year, month, day,
0, 0, 0, 0);
if (midnightObj == NULL) {
return false;
}
timestampObj = PyObject_CallMethodObjArgs(midnightObj, timestamp_name,
NULL);
Py_DECREF(midnightObj);
if (timestampObj == NULL) {
return false;
}
double timestamp = PyFloat_AsDouble(timestampObj);
Py_DECREF(timestampObj);
if (datetimeMode & DM_ONLY_SECONDS) {
writer->Int64((int64_t) timestamp);
} else {
// Writer.SetMaxDecimalPlaces(6) truncates the value,
// so for example 1514893636.276703 would come out as
// 1514893636.276702, because its exact double value is
// 1514893636.2767028808593750000000000...
char tsStr[12 + 1 + 6 + 1];
// Temporarily switch to a POSIX locale, in case the outer
// world is configured differently, see above
const char* locale = setlocale(LC_NUMERIC, NULL);
setlocale(LC_NUMERIC, "C");
setlocale(LC_NUMERIC, locale);
int size = snprintf(tsStr, 12 + 1 + 6, "%.6f", timestamp);
// Remove trailing 0s
while (tsStr[size-2] != '.' && tsStr[size-1] == '0')
size--;
writer->RawValue(tsStr, size, kNumberType);
}
}
} else if (uuidMode != UM_NONE
&& PyObject_TypeCheck(object, (PyTypeObject*) uuid_type)) {
PyObject* hexval;
if (uuidMode == UM_CANONICAL)
hexval = PyObject_Str(object);
else
hexval = PyObject_GetAttr(object, hex_name);
if (hexval == NULL)
return false;
Py_ssize_t size;
const char* s = PyUnicode_AsUTF8AndSize(hexval, &size);
if (s == NULL) {
Py_DECREF(hexval);
return false;
}
if (RAPIDJSON_UNLIKELY(size != 32 && size != 36)) {
PyErr_Format(PyExc_ValueError,
"Bad UUID hex, expected a string of either 32 or 36 chars,"
" got %.200R", hexval);
Py_DECREF(hexval);
return false;
}
char quoted[39];
quoted[0] = quoted[size + 1] = '"';
memcpy(quoted + 1, s, size);
writer->RawValue(quoted, (SizeType) size + 2, kStringType);
Py_DECREF(hexval);
} else if (!(iterableMode & IM_ONLY_LISTS) && PyIter_Check(object)) {
PyObject* iterator = PyObject_GetIter(object);
if (iterator == NULL)
return false;
writer->StartArray();
PyObject* item;
while ((item = PyIter_Next(iterator))) {
if (Py_EnterRecursiveCall(" while JSONifying iterable object")) {
Py_DECREF(item);
Py_DECREF(iterator);
return false;
}
bool r = RECURSE(item);
Py_LeaveRecursiveCall();
Py_DECREF(item);
if (!r) {
Py_DECREF(iterator);
return false;
}
}
Py_DECREF(iterator);
// PyIter_Next() may exit with an error
if (PyErr_Occurred())
return false;
writer->EndArray();
} else if (PyObject_TypeCheck(object, &RawJSON_Type)) {
const char* jsonStr;
Py_ssize_t l;
jsonStr = PyUnicode_AsUTF8AndSize(((RawJSON*) object)->value, &l);
if (jsonStr == NULL)
return false;
ASSERT_VALID_SIZE(l);
writer->RawValue(jsonStr, (SizeType) l, kStringType);
} else if (defaultFn) {
PyObject* retval = PyObject_CallFunctionObjArgs(defaultFn, object, NULL);
if (retval == NULL)
return false;
if (Py_EnterRecursiveCall(" while JSONifying default function result")) {
Py_DECREF(retval);
return false;
}
bool r = RECURSE(retval);
Py_LeaveRecursiveCall();
Py_DECREF(retval);
if (!r)
return false;
} else {
PyErr_Format(PyExc_TypeError, "%R is not JSON serializable", object);
return false;
}
// Catch possible error raised in associated stream operations
return PyErr_Occurred() ? false : true;
#undef RECURSE
#undef ASSERT_VALID_SIZE
}
typedef struct {
PyObject_HEAD
bool ensureAscii;
unsigned writeMode;
char indentChar;
unsigned indentCount;
unsigned datetimeMode;
unsigned uuidMode;
unsigned numberMode;
unsigned bytesMode;
unsigned iterableMode;
unsigned mappingMode;
} EncoderObject;
PyDoc_STRVAR(dumps_docstring,
"dumps(obj, *, skipkeys=False, ensure_ascii=True, write_mode=WM_COMPACT,"
" indent=4, default=None, sort_keys=False, number_mode=None,"
" datetime_mode=None, uuid_mode=None, bytes_mode=BM_UTF8,"
" iterable_mode=IM_ANY_ITERABLE, mapping_mode=MM_ANY_MAPPING,"
" allow_nan=True)\n"
"\n"
"Encode a Python object into a JSON string.");
static PyObject*
dumps(PyObject* self, PyObject* args, PyObject* kwargs)
{
/* Converts a Python object to a JSON-encoded string. */
PyObject* value;
int ensureAscii = true;
PyObject* indent = NULL;
PyObject* defaultFn = NULL;
PyObject* numberModeObj = NULL;
unsigned numberMode = NM_NAN;
PyObject* datetimeModeObj = NULL;
unsigned datetimeMode = DM_NONE;
PyObject* uuidModeObj = NULL;
unsigned uuidMode = UM_NONE;
PyObject* bytesModeObj = NULL;
unsigned bytesMode = BM_UTF8;
PyObject* writeModeObj = NULL;
unsigned writeMode = WM_COMPACT;
PyObject* iterableModeObj = NULL;
unsigned iterableMode = IM_ANY_ITERABLE;
PyObject* mappingModeObj = NULL;
unsigned mappingMode = MM_ANY_MAPPING;
char indentChar = ' ';
unsigned indentCount = 4;
static char const* kwlist[] = {
"obj",
"skipkeys", // alias of MM_SKIP_NON_STRING_KEYS
"ensure_ascii",
"indent",
"default",
"sort_keys", // alias of MM_SORT_KEYS
"number_mode",
"datetime_mode",
"uuid_mode",
"bytes_mode",
"write_mode",
"iterable_mode",
"mapping_mode",
/* compatibility with stdlib json */
"allow_nan",
NULL
};
int skipKeys = false;
int sortKeys = false;
int allowNan = -1;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|$ppOOpOOOOOOOp:rapidjson.dumps",
(char**) kwlist,
&value,
&skipKeys,
&ensureAscii,
&indent,
&defaultFn,
&sortKeys,
&numberModeObj,
&datetimeModeObj,
&uuidModeObj,
&bytesModeObj,
&writeModeObj,
&iterableModeObj,
&mappingModeObj,
&allowNan))
return NULL;
if (defaultFn && !PyCallable_Check(defaultFn)) {
if (defaultFn == Py_None) {
defaultFn = NULL;
} else {
PyErr_SetString(PyExc_TypeError, "default must be a callable");
return NULL;
}
}
if (!accept_indent_arg(indent, writeMode, indentCount, indentChar))
return NULL;
if (!accept_write_mode_arg(writeModeObj, writeMode))
return NULL;
if (!accept_number_mode_arg(numberModeObj, allowNan, numberMode))
return NULL;
if (!accept_datetime_mode_arg(datetimeModeObj, datetimeMode))
return NULL;
if (!accept_uuid_mode_arg(uuidModeObj, uuidMode))
return NULL;
if (!accept_bytes_mode_arg(bytesModeObj, bytesMode))
return NULL;
if (!accept_iterable_mode_arg(iterableModeObj, iterableMode))
return NULL;
if (!accept_mapping_mode_arg(mappingModeObj, mappingMode))
return NULL;
if (skipKeys)
mappingMode |= MM_SKIP_NON_STRING_KEYS;
if (sortKeys)
mappingMode |= MM_SORT_KEYS;
return do_encode(value, defaultFn, ensureAscii ? true : false, writeMode, indentChar,
indentCount, numberMode, datetimeMode, uuidMode, bytesMode,
iterableMode, mappingMode);
}
PyDoc_STRVAR(dump_docstring,
"dump(obj, stream, *, skipkeys=False, ensure_ascii=True,"
" write_mode=WM_COMPACT, indent=4, default=None, sort_keys=False,"
" number_mode=None, datetime_mode=None, uuid_mode=None, bytes_mode=BM_UTF8,"
" iterable_mode=IM_ANY_ITERABLE, mapping_mode=MM_ANY_MAPPING,"
" chunk_size=65536, allow_nan=True)\n"
"\n"
"Encode a Python object into a JSON stream.");
static PyObject*
dump(PyObject* self, PyObject* args, PyObject* kwargs)
{
/* Converts a Python object to a JSON-encoded stream. */
PyObject* value;
PyObject* stream;
int ensureAscii = true;
PyObject* indent = NULL;
PyObject* defaultFn = NULL;
PyObject* numberModeObj = NULL;
unsigned numberMode = NM_NAN;
PyObject* datetimeModeObj = NULL;
unsigned datetimeMode = DM_NONE;
PyObject* uuidModeObj = NULL;
unsigned uuidMode = UM_NONE;
PyObject* bytesModeObj = NULL;
unsigned bytesMode = BM_UTF8;
PyObject* writeModeObj = NULL;
unsigned writeMode = WM_COMPACT;
PyObject* iterableModeObj = NULL;
unsigned iterableMode = IM_ANY_ITERABLE;
PyObject* mappingModeObj = NULL;
unsigned mappingMode = MM_ANY_MAPPING;
char indentChar = ' ';
unsigned indentCount = 4;
PyObject* chunkSizeObj = NULL;
size_t chunkSize = 65536;
int allowNan = -1;
static char const* kwlist[] = {
"obj",
"stream",
"skipkeys", // alias of MM_SKIP_NON_STRING_KEYS
"ensure_ascii",
"indent",
"default",
"sort_keys", // alias of MM_SORT_KEYS
"number_mode",
"datetime_mode",
"uuid_mode",
"bytes_mode",
"chunk_size",
"write_mode",
"iterable_mode",
"mapping_mode",
/* compatibility with stdlib json */
"allow_nan",
NULL
};
int skipKeys = false;
int sortKeys = false;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|$ppOOpOOOOOOOOp:rapidjson.dump",
(char**) kwlist,
&value,
&stream,
&skipKeys,
&ensureAscii,
&indent,
&defaultFn,
&sortKeys,
&numberModeObj,
&datetimeModeObj,
&uuidModeObj,
&bytesModeObj,
&chunkSizeObj,
&writeModeObj,
&iterableModeObj,
&mappingModeObj,
&allowNan))
return NULL;
if (defaultFn && !PyCallable_Check(defaultFn)) {
if (defaultFn == Py_None) {
defaultFn = NULL;
} else {
PyErr_SetString(PyExc_TypeError, "default must be a callable");
return NULL;
}
}
if (!accept_indent_arg(indent, writeMode, indentCount, indentChar))
return NULL;
if (!accept_write_mode_arg(writeModeObj, writeMode))
return NULL;
if (!accept_number_mode_arg(numberModeObj, allowNan, numberMode))
return NULL;
if (!accept_datetime_mode_arg(datetimeModeObj, datetimeMode))
return NULL;
if (!accept_uuid_mode_arg(uuidModeObj, uuidMode))
return NULL;
if (!accept_bytes_mode_arg(bytesModeObj, bytesMode))
return NULL;
if (!accept_chunk_size_arg(chunkSizeObj, chunkSize))
return NULL;
if (!accept_iterable_mode_arg(iterableModeObj, iterableMode))
return NULL;
if (!accept_mapping_mode_arg(mappingModeObj, mappingMode))
return NULL;
if (skipKeys)
mappingMode |= MM_SKIP_NON_STRING_KEYS;
if (sortKeys)
mappingMode |= MM_SORT_KEYS;
return do_stream_encode(value, stream, chunkSize, defaultFn,
ensureAscii ? true : false, writeMode, indentChar,
indentCount, numberMode, datetimeMode, uuidMode, bytesMode,
iterableMode, mappingMode);
}
PyDoc_STRVAR(encoder_doc,
"Encoder(skip_invalid_keys=False, ensure_ascii=True, write_mode=WM_COMPACT,"
" indent=4, sort_keys=False, number_mode=None, datetime_mode=None,"
" uuid_mode=None, bytes_mode=None, iterable_mode=IM_ANY_ITERABLE,"
" mapping_mode=MM_ANY_MAPPING)\n\n"
"Create and return a new Encoder instance.");
static PyMemberDef encoder_members[] = {
{"ensure_ascii",
T_BOOL, offsetof(EncoderObject, ensureAscii), READONLY,
"whether the output should contain only ASCII characters."},
{"indent_char",
T_CHAR, offsetof(EncoderObject, indentChar), READONLY,
"What will be used as end-of-line character."},
{"indent_count",
T_UINT, offsetof(EncoderObject, indentCount), READONLY,
"The indentation width."},
{"datetime_mode",
T_UINT, offsetof(EncoderObject, datetimeMode), READONLY,
"Whether and how datetime values should be encoded."},
{"uuid_mode",
T_UINT, offsetof(EncoderObject, uuidMode), READONLY,
"Whether and how UUID values should be encoded"},
{"number_mode",
T_UINT, offsetof(EncoderObject, numberMode), READONLY,
"The encoding behavior with regards to numeric values."},
{"bytes_mode",
T_UINT, offsetof(EncoderObject, bytesMode), READONLY,
"How bytes values should be treated."},
{"write_mode",
T_UINT, offsetof(EncoderObject, writeMode), READONLY,
"Whether the output should be pretty printed or not."},
{"iterable_mode",
T_UINT, offsetof(EncoderObject, iterableMode), READONLY,
"Whether iterable values other than lists shall be encoded as JSON arrays or not."},
{"mapping_mode",
T_UINT, offsetof(EncoderObject, mappingMode), READONLY,
"Whether mapping values other than dicts shall be encoded as JSON objects or not."},
{NULL}
};
static PyObject*
encoder_get_skip_invalid_keys(EncoderObject* e, void* closure)
{
return PyBool_FromLong(e->mappingMode & MM_SKIP_NON_STRING_KEYS);
}
static PyObject*
encoder_get_sort_keys(EncoderObject* e, void* closure)
{
return PyBool_FromLong(e->mappingMode & MM_SORT_KEYS);
}
// Backward compatibility, previously they were members of EncoderObject
static PyGetSetDef encoder_props[] = {
{"skip_invalid_keys", (getter) encoder_get_skip_invalid_keys, NULL,
"Whether invalid keys shall be skipped."},
{"sort_keys", (getter) encoder_get_sort_keys, NULL,
"Whether dictionary keys shall be sorted alphabetically."},
{NULL}
};
static PyTypeObject Encoder_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"rapidjson.Encoder", /* tp_name */
sizeof(EncoderObject), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(ternaryfunc) encoder_call, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
encoder_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
encoder_members, /* tp_members */
encoder_props, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
encoder_new, /* tp_new */
PyObject_Del, /* tp_free */
};
#define Encoder_CheckExact(v) (Py_TYPE(v) == &Encoder_Type)
#define Encoder_Check(v) PyObject_TypeCheck(v, &Encoder_Type)
#define DUMPS_INTERNAL_CALL \
(dumps_internal(&writer, \
value, \
defaultFn, \
numberMode, \
datetimeMode, \
uuidMode, \
bytesMode, \
iterableMode, \
mappingMode) \
? PyUnicode_FromString(buf.GetString()) : NULL)
static PyObject*
do_encode(PyObject* value, PyObject* defaultFn, bool ensureAscii, unsigned writeMode,
char indentChar, unsigned indentCount, unsigned numberMode,
unsigned datetimeMode, unsigned uuidMode, unsigned bytesMode,
unsigned iterableMode, unsigned mappingMode)
{
if (writeMode == WM_COMPACT) {
if (ensureAscii) {
GenericStringBuffer buf;
Writer writer(buf);
return DUMPS_INTERNAL_CALL;
} else {
StringBuffer buf;
Writer writer(buf);
return DUMPS_INTERNAL_CALL;
}
} else if (ensureAscii) {
GenericStringBuffer buf;
PrettyWriter writer(buf);
writer.SetIndent(indentChar, indentCount);
if (writeMode & WM_SINGLE_LINE_ARRAY) {
writer.SetFormatOptions(kFormatSingleLineArray);
}
return DUMPS_INTERNAL_CALL;
} else {
StringBuffer buf;
PrettyWriter writer(buf);
writer.SetIndent(indentChar, indentCount);
if (writeMode & WM_SINGLE_LINE_ARRAY) {
writer.SetFormatOptions(kFormatSingleLineArray);
}
return DUMPS_INTERNAL_CALL;
}
}
#define DUMP_INTERNAL_CALL \
(dumps_internal(&writer, \
value, \
defaultFn, \
numberMode, \
datetimeMode, \
uuidMode, \
bytesMode, \
iterableMode, \
mappingMode) \
? Py_INCREF(Py_None), Py_None : NULL)
static PyObject*
do_stream_encode(PyObject* value, PyObject* stream, size_t chunkSize, PyObject* defaultFn,
bool ensureAscii, unsigned writeMode, char indentChar,
unsigned indentCount, unsigned numberMode, unsigned datetimeMode,
unsigned uuidMode, unsigned bytesMode, unsigned iterableMode,
unsigned mappingMode)
{
PyWriteStreamWrapper os(stream, chunkSize);
if (writeMode == WM_COMPACT) {
if (ensureAscii) {
Writer writer(os);
return DUMP_INTERNAL_CALL;
} else {
Writer writer(os);
return DUMP_INTERNAL_CALL;
}
} else if (ensureAscii) {
PrettyWriter writer(os);
writer.SetIndent(indentChar, indentCount);
if (writeMode & WM_SINGLE_LINE_ARRAY) {
writer.SetFormatOptions(kFormatSingleLineArray);
}
return DUMP_INTERNAL_CALL;
} else {
PrettyWriter writer(os);
writer.SetIndent(indentChar, indentCount);
if (writeMode & WM_SINGLE_LINE_ARRAY) {
writer.SetFormatOptions(kFormatSingleLineArray);
}
return DUMP_INTERNAL_CALL;
}
}
static PyObject*
encoder_call(PyObject* self, PyObject* args, PyObject* kwargs)
{
static char const* kwlist[] = {
"obj",
"stream",
"chunk_size",
NULL
};
PyObject* value;
PyObject* stream = NULL;
PyObject* chunkSizeObj = NULL;
size_t chunkSize = 65536;
PyObject* defaultFn = NULL;
PyObject* result;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O$O",
(char**) kwlist,
&value,
&stream,
&chunkSizeObj))
return NULL;
EncoderObject* e = (EncoderObject*) self;
if (stream != NULL && stream != Py_None) {
if (!PyObject_HasAttr(stream, write_name)) {
PyErr_SetString(PyExc_TypeError, "Expected a writable stream");
return NULL;
}
if (!accept_chunk_size_arg(chunkSizeObj, chunkSize))
return NULL;
if (PyObject_HasAttr(self, default_name)) {
defaultFn = PyObject_GetAttr(self, default_name);
}
result = do_stream_encode(value, stream, chunkSize, defaultFn, e->ensureAscii,
e->writeMode, e->indentChar, e->indentCount,
e->numberMode, e->datetimeMode, e->uuidMode,
e->bytesMode, e->iterableMode, e->mappingMode);
} else {
if (PyObject_HasAttr(self, default_name)) {
defaultFn = PyObject_GetAttr(self, default_name);
}
result = do_encode(value, defaultFn, e->ensureAscii, e->writeMode, e->indentChar,
e->indentCount, e->numberMode, e->datetimeMode, e->uuidMode,
e->bytesMode, e->iterableMode, e->mappingMode);
}
if (defaultFn != NULL)
Py_DECREF(defaultFn);
return result;
}
static PyObject*
encoder_new(PyTypeObject* type, PyObject* args, PyObject* kwargs)
{
EncoderObject* e;
int ensureAscii = true;
PyObject* indent = NULL;
PyObject* numberModeObj = NULL;
unsigned numberMode = NM_NAN;
PyObject* datetimeModeObj = NULL;
unsigned datetimeMode = DM_NONE;
PyObject* uuidModeObj = NULL;
unsigned uuidMode = UM_NONE;
PyObject* bytesModeObj = NULL;
unsigned bytesMode = BM_UTF8;
PyObject* writeModeObj = NULL;
unsigned writeMode = WM_COMPACT;
PyObject* iterableModeObj = NULL;
unsigned iterableMode = IM_ANY_ITERABLE;
PyObject* mappingModeObj = NULL;
unsigned mappingMode = MM_ANY_MAPPING;
char indentChar = ' ';
unsigned indentCount = 4;
static char const* kwlist[] = {
"skip_invalid_keys", // alias of MM_SKIP_NON_STRING_KEYS
"ensure_ascii",
"indent",
"sort_keys", // alias of MM_SORT_KEYS
"number_mode",
"datetime_mode",
"uuid_mode",
"bytes_mode",
"write_mode",
"iterable_mode",
"mapping_mode",
NULL
};
int skipInvalidKeys = false;
int sortKeys = false;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ppOpOOOOOOO:Encoder",
(char**) kwlist,
&skipInvalidKeys,
&ensureAscii,
&indent,
&sortKeys,
&numberModeObj,
&datetimeModeObj,
&uuidModeObj,
&bytesModeObj,
&writeModeObj,
&iterableModeObj,
&mappingModeObj))
return NULL;
if (!accept_indent_arg(indent, writeMode, indentCount, indentChar))
return NULL;
if (!accept_write_mode_arg(writeModeObj, writeMode))
return NULL;
if (!accept_number_mode_arg(numberModeObj, -1, numberMode))
return NULL;
if (!accept_datetime_mode_arg(datetimeModeObj, datetimeMode))
return NULL;
if (!accept_uuid_mode_arg(uuidModeObj, uuidMode))
return NULL;
if (!accept_bytes_mode_arg(bytesModeObj, bytesMode))
return NULL;
if (!accept_iterable_mode_arg(iterableModeObj, iterableMode))
return NULL;
if (!accept_mapping_mode_arg(mappingModeObj, mappingMode))
return NULL;
if (skipInvalidKeys)
mappingMode |= MM_SKIP_NON_STRING_KEYS;
if (sortKeys)
mappingMode |= MM_SORT_KEYS;
e = (EncoderObject*) type->tp_alloc(type, 0);
if (e == NULL)
return NULL;
e->ensureAscii = ensureAscii ? true : false;
e->writeMode = writeMode;
e->indentChar = indentChar;
e->indentCount = indentCount;
e->datetimeMode = datetimeMode;
e->uuidMode = uuidMode;
e->numberMode = numberMode;
e->bytesMode = bytesMode;
e->iterableMode = iterableMode;
e->mappingMode = mappingMode;
return (PyObject*) e;
}
///////////////
// Validator //
///////////////
typedef struct {
PyObject_HEAD
SchemaDocument *schema;
} ValidatorObject;
PyDoc_STRVAR(validator_doc,
"Validator(json_schema)\n"
"\n"
"Create and return a new Validator instance from the given `json_schema`"
" string.");
static PyTypeObject Validator_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"rapidjson.Validator", /* tp_name */
sizeof(ValidatorObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor) validator_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(ternaryfunc) validator_call, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
validator_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
validator_new, /* tp_new */
PyObject_Del, /* tp_free */
};
static PyObject* validator_call(PyObject* self, PyObject* args, PyObject* kwargs)
{
PyObject* jsonObject;
if (!PyArg_ParseTuple(args, "O", &jsonObject))
return NULL;
const char* jsonStr;
PyObject* asUnicode = NULL;
if (PyUnicode_Check(jsonObject)) {
jsonStr = PyUnicode_AsUTF8(jsonObject);
if (jsonStr == NULL)
return NULL;
} else if (PyBytes_Check(jsonObject) || PyByteArray_Check(jsonObject)) {
asUnicode = PyUnicode_FromEncodedObject(jsonObject, "utf-8", NULL);
if (asUnicode == NULL)
return NULL;
jsonStr = PyUnicode_AsUTF8(asUnicode);
if (jsonStr == NULL) {
Py_DECREF(asUnicode);
return NULL;
}
} else {
PyErr_SetString(PyExc_TypeError,
"Expected string or UTF-8 encoded bytes or bytearray");
return NULL;
}
Document d;
bool error;
Py_BEGIN_ALLOW_THREADS
error = d.Parse(jsonStr).HasParseError();
Py_END_ALLOW_THREADS
if (error) {
if (asUnicode != NULL)
Py_DECREF(asUnicode);
PyErr_SetString(decode_error, "Invalid JSON");
return NULL;
}
SchemaValidator validator(*((ValidatorObject*) self)->schema);
bool accept;
Py_BEGIN_ALLOW_THREADS
accept = d.Accept(validator);
Py_END_ALLOW_THREADS
if (asUnicode != NULL)
Py_DECREF(asUnicode);
if (!accept) {
StringBuffer sptr;
StringBuffer dptr;
Py_BEGIN_ALLOW_THREADS
validator.GetInvalidSchemaPointer().StringifyUriFragment(sptr);
validator.GetInvalidDocumentPointer().StringifyUriFragment(dptr);
Py_END_ALLOW_THREADS
PyObject* error = Py_BuildValue("sss", validator.GetInvalidSchemaKeyword(),
sptr.GetString(), dptr.GetString());
PyErr_SetObject(validation_error, error);
if (error != NULL)
Py_DECREF(error);
sptr.Clear();
dptr.Clear();
return NULL;
}
Py_RETURN_NONE;
}
static void validator_dealloc(PyObject* self)
{
ValidatorObject* s = (ValidatorObject*) self;
delete s->schema;
Py_TYPE(self)->tp_free(self);
}
static PyObject* validator_new(PyTypeObject* type, PyObject* args, PyObject* kwargs)
{
PyObject* jsonObject;
if (!PyArg_ParseTuple(args, "O", &jsonObject))
return NULL;
const char* jsonStr;
PyObject* asUnicode = NULL;
if (PyUnicode_Check(jsonObject)) {
jsonStr = PyUnicode_AsUTF8(jsonObject);
if (jsonStr == NULL)
return NULL;
} else if (PyBytes_Check(jsonObject) || PyByteArray_Check(jsonObject)) {
asUnicode = PyUnicode_FromEncodedObject(jsonObject, "utf-8", NULL);
if (asUnicode == NULL)
return NULL;
jsonStr = PyUnicode_AsUTF8(asUnicode);
if (jsonStr == NULL) {
Py_DECREF(asUnicode);
return NULL;
}
} else {
PyErr_SetString(PyExc_TypeError,
"Expected string or UTF-8 encoded bytes or bytearray");
return NULL;
}
Document d;
bool error;
Py_BEGIN_ALLOW_THREADS
error = d.Parse(jsonStr).HasParseError();
Py_END_ALLOW_THREADS
if (asUnicode != NULL)
Py_DECREF(asUnicode);
if (error) {
PyErr_SetString(decode_error, "Invalid JSON");
return NULL;
}
ValidatorObject* v = (ValidatorObject*) type->tp_alloc(type, 0);
if (v == NULL)
return NULL;
v->schema = new SchemaDocument(d);
return (PyObject*) v;
}
////////////
// Module //
////////////
static PyMethodDef functions[] = {
{"loads", (PyCFunction) loads, METH_VARARGS | METH_KEYWORDS,
loads_docstring},
{"load", (PyCFunction) load, METH_VARARGS | METH_KEYWORDS,
load_docstring},
{"dumps", (PyCFunction) dumps, METH_VARARGS | METH_KEYWORDS,
dumps_docstring},
{"dump", (PyCFunction) dump, METH_VARARGS | METH_KEYWORDS,
dump_docstring},
{NULL, NULL, 0, NULL} /* sentinel */
};
static int
module_exec(PyObject* m)
{
PyObject* datetimeModule;
PyObject* decimalModule;
PyObject* uuidModule;
if (PyType_Ready(&Decoder_Type) < 0)
return -1;
if (PyType_Ready(&Encoder_Type) < 0)
return -1;
if (PyType_Ready(&Validator_Type) < 0)
return -1;
if (PyType_Ready(&RawJSON_Type) < 0)
return -1;
PyDateTime_IMPORT;
if(!PyDateTimeAPI)
return -1;
datetimeModule = PyImport_ImportModule("datetime");
if (datetimeModule == NULL)
return -1;
decimalModule = PyImport_ImportModule("decimal");
if (decimalModule == NULL)
return -1;
decimal_type = PyObject_GetAttrString(decimalModule, "Decimal");
Py_DECREF(decimalModule);
if (decimal_type == NULL)
return -1;
timezone_type = PyObject_GetAttrString(datetimeModule, "timezone");
Py_DECREF(datetimeModule);
if (timezone_type == NULL)
return -1;
timezone_utc = PyObject_GetAttrString(timezone_type, "utc");
if (timezone_utc == NULL)
return -1;
uuidModule = PyImport_ImportModule("uuid");
if (uuidModule == NULL)
return -1;
uuid_type = PyObject_GetAttrString(uuidModule, "UUID");
Py_DECREF(uuidModule);
if (uuid_type == NULL)
return -1;
astimezone_name = PyUnicode_InternFromString("astimezone");
if (astimezone_name == NULL)
return -1;
hex_name = PyUnicode_InternFromString("hex");
if (hex_name == NULL)
return -1;
timestamp_name = PyUnicode_InternFromString("timestamp");
if (timestamp_name == NULL)
return -1;
total_seconds_name = PyUnicode_InternFromString("total_seconds");
if (total_seconds_name == NULL)
return -1;
utcoffset_name = PyUnicode_InternFromString("utcoffset");
if (utcoffset_name == NULL)
return -1;
is_infinite_name = PyUnicode_InternFromString("is_infinite");
if (is_infinite_name == NULL)
return -1;
is_nan_name = PyUnicode_InternFromString("is_nan");
if (is_infinite_name == NULL)
return -1;
minus_inf_string_value = PyUnicode_InternFromString("-Infinity");
if (minus_inf_string_value == NULL)
return -1;
nan_string_value = PyUnicode_InternFromString("nan");
if (nan_string_value == NULL)
return -1;
plus_inf_string_value = PyUnicode_InternFromString("+Infinity");
if (plus_inf_string_value == NULL)
return -1;
start_object_name = PyUnicode_InternFromString("start_object");
if (start_object_name == NULL)
return -1;
end_object_name = PyUnicode_InternFromString("end_object");
if (end_object_name == NULL)
return -1;
default_name = PyUnicode_InternFromString("default");
if (default_name == NULL)
return -1;
end_array_name = PyUnicode_InternFromString("end_array");
if (end_array_name == NULL)
return -1;
string_name = PyUnicode_InternFromString("string");
if (string_name == NULL)
return -1;
read_name = PyUnicode_InternFromString("read");
if (read_name == NULL)
return -1;
write_name = PyUnicode_InternFromString("write");
if (write_name == NULL)
return -1;
encoding_name = PyUnicode_InternFromString("encoding");
if (encoding_name == NULL)
return -1;
#define STRINGIFY(x) XSTRINGIFY(x)
#define XSTRINGIFY(x) #x
if (PyModule_AddIntConstant(m, "DM_NONE", DM_NONE)
|| PyModule_AddIntConstant(m, "DM_ISO8601", DM_ISO8601)
|| PyModule_AddIntConstant(m, "DM_UNIX_TIME", DM_UNIX_TIME)
|| PyModule_AddIntConstant(m, "DM_ONLY_SECONDS", DM_ONLY_SECONDS)
|| PyModule_AddIntConstant(m, "DM_IGNORE_TZ", DM_IGNORE_TZ)
|| PyModule_AddIntConstant(m, "DM_NAIVE_IS_UTC", DM_NAIVE_IS_UTC)
|| PyModule_AddIntConstant(m, "DM_SHIFT_TO_UTC", DM_SHIFT_TO_UTC)
|| PyModule_AddIntConstant(m, "UM_NONE", UM_NONE)
|| PyModule_AddIntConstant(m, "UM_HEX", UM_HEX)
|| PyModule_AddIntConstant(m, "UM_CANONICAL", UM_CANONICAL)
|| PyModule_AddIntConstant(m, "NM_NONE", NM_NONE)
|| PyModule_AddIntConstant(m, "NM_NAN", NM_NAN)
|| PyModule_AddIntConstant(m, "NM_DECIMAL", NM_DECIMAL)
|| PyModule_AddIntConstant(m, "NM_NATIVE", NM_NATIVE)
|| PyModule_AddIntConstant(m, "PM_NONE", PM_NONE)
|| PyModule_AddIntConstant(m, "PM_COMMENTS", PM_COMMENTS)
|| PyModule_AddIntConstant(m, "PM_TRAILING_COMMAS", PM_TRAILING_COMMAS)
|| PyModule_AddIntConstant(m, "BM_NONE", BM_NONE)
|| PyModule_AddIntConstant(m, "BM_UTF8", BM_UTF8)
|| PyModule_AddIntConstant(m, "WM_COMPACT", WM_COMPACT)
|| PyModule_AddIntConstant(m, "WM_PRETTY", WM_PRETTY)
|| PyModule_AddIntConstant(m, "WM_SINGLE_LINE_ARRAY", WM_SINGLE_LINE_ARRAY)
|| PyModule_AddIntConstant(m, "IM_ANY_ITERABLE", IM_ANY_ITERABLE)
|| PyModule_AddIntConstant(m, "IM_ONLY_LISTS", IM_ONLY_LISTS)
|| PyModule_AddIntConstant(m, "MM_ANY_MAPPING", MM_ANY_MAPPING)
|| PyModule_AddIntConstant(m, "MM_ONLY_DICTS", MM_ONLY_DICTS)
|| PyModule_AddIntConstant(m, "MM_COERCE_KEYS_TO_STRINGS",
MM_COERCE_KEYS_TO_STRINGS)
|| PyModule_AddIntConstant(m, "MM_SKIP_NON_STRING_KEYS", MM_SKIP_NON_STRING_KEYS)
|| PyModule_AddIntConstant(m, "MM_SORT_KEYS", MM_SORT_KEYS)
|| PyModule_AddStringConstant(m, "__version__",
STRINGIFY(PYTHON_RAPIDJSON_VERSION))
|| PyModule_AddStringConstant(m, "__author__",
"Ken Robbins "
", Lele Gaifax ")
|| PyModule_AddStringConstant(m, "__rapidjson_version__",
RAPIDJSON_VERSION_STRING)
|| PyModule_AddStringConstant(m, "__rapidjson_exact_version__",
#ifdef RAPIDJSON_EXACT_VERSION
STRINGIFY(RAPIDJSON_EXACT_VERSION)
#else
// This may happen for several reasons, under CI
// test or when the RJ library does not come from
// the git submodule
"not available"
#endif
)
)
return -1;
Py_INCREF(&Decoder_Type);
if (PyModule_AddObject(m, "Decoder", (PyObject*) &Decoder_Type) < 0) {
Py_DECREF(&Decoder_Type);
return -1;
}
Py_INCREF(&Encoder_Type);
if (PyModule_AddObject(m, "Encoder", (PyObject*) &Encoder_Type) < 0) {
Py_DECREF(&Encoder_Type);
return -1;
}
Py_INCREF(&Validator_Type);
if (PyModule_AddObject(m, "Validator", (PyObject*) &Validator_Type) < 0) {
Py_DECREF(&Validator_Type);
return -1;
}
Py_INCREF(&RawJSON_Type);
if (PyModule_AddObject(m, "RawJSON", (PyObject*) &RawJSON_Type) < 0) {
Py_DECREF(&RawJSON_Type);
return -1;
}
validation_error = PyErr_NewException("rapidjson.ValidationError",
PyExc_ValueError, NULL);
if (validation_error == NULL)
return -1;
Py_INCREF(validation_error);
if (PyModule_AddObject(m, "ValidationError", validation_error) < 0) {
Py_DECREF(validation_error);
return -1;
}
decode_error = PyErr_NewException("rapidjson.JSONDecodeError",
PyExc_ValueError, NULL);
if (decode_error == NULL)
return -1;
Py_INCREF(decode_error);
if (PyModule_AddObject(m, "JSONDecodeError", decode_error) < 0) {
Py_DECREF(decode_error);
return -1;
}
return 0;
}
static struct PyModuleDef_Slot slots[] = {
{Py_mod_exec, (void*) module_exec},
{0, NULL}
};
static PyModuleDef module = {
PyModuleDef_HEAD_INIT, /* m_base */
"rapidjson", /* m_name */
PyDoc_STR("Fast, simple JSON encoder and decoder. Based on RapidJSON C++ library."),
0, /* m_size */
functions, /* m_methods */
slots, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL /* m_free */
};
PyMODINIT_FUNC
PyInit_rapidjson()
{
return PyModuleDef_Init(&module);
}