1
0
mirror of https://github.com/ultrajson/ultrajson.git synced 2024-11-23 16:42:10 +01:00

Added support for python decimal. Forward ported encode_html_chars patch. Made it all into one big nice commit

This commit is contained in:
jtarnstrom 2013-02-12 17:13:23 +01:00
parent c5c3c7fbbc
commit fd6cc28ab9
6 changed files with 86 additions and 12 deletions

@ -225,6 +225,9 @@ typedef struct __JSONObjectEncoder
If true output will be ASCII with all characters above 127 encoded as \uXXXX. If false output will be UTF-8 or what ever charset strings are brought as */
int forceASCII;
/*
If true, '<', '>', and '&' characters will be encoded as \u003c, \u003e, and \u0026, respectively. If false, no special encoding will be used. */
int encodeHTMLChars;
/*
Set to an error message if error occured */

@ -65,8 +65,8 @@ static const JSUINT8 g_asciiOutputTable[256] =
{
/* 0x00 */ 0, 30, 30, 30, 30, 30, 30, 30, 10, 12, 14, 30, 16, 18, 30, 30,
/* 0x10 */ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
/* 0x20 */ 1, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 24,
/* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x20 */ 1, 1, 20, 1, 1, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, 24,
/* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, 1, 29, 1,
/* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22, 1, 1, 1,
/* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -169,6 +169,20 @@ int Buffer_EscapeStringUnvalidated (JSONObjectEncoder *enc, const char *io, cons
case '\r': (*of++) = '\\'; (*of++) = 'r'; break;
case '\t': (*of++) = '\\'; (*of++) = 't'; break;
case 0x26: // '/'
case 0x3c: // '<'
case 0x3e: // '>'
if (enc->encodeHTMLChars)
{
// Fall through to \u00XX case below.
}
else
{
// Same as default case below.
(*of++) = (*io);
break;
}
case 0x01:
case 0x02:
case 0x03:
@ -352,6 +366,18 @@ int Buffer_EscapeStringValidated (JSOBJ obj, JSONObjectEncoder *enc, const char
SetError (obj, enc, "Unsupported UTF-8 sequence length when encoding string");
return FALSE;
case 29:
if (enc->encodeHTMLChars)
{
// Fall through to \u00XX case 30 below.
}
else
{
// Same as case 1 above.
*(of++) = (*io++);
continue;
}
case 30:
// \uXXXX encode
*(of++) = '\\';

@ -41,6 +41,7 @@ http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
#include <ultrajson.h>
#define EPOCH_ORD 719163
static PyObject* type_decimal;
typedef void *(*PFN_PyTypeToJSON)(JSOBJ obj, JSONTypeContext *ti, void *outValue, size_t *_outLen);
@ -86,6 +87,11 @@ struct PyDictIterState
void initObjToJSON(void)
{
PyObject* mod_decimal = PyImport_ImportModule("decimal");
type_decimal = PyObject_GetAttrString(mod_decimal, "Decimal");
Py_INCREF(type_decimal);
Py_DECREF(mod_decimal);
PyDateTime_IMPORT;
}
@ -112,7 +118,7 @@ static void *PyLongToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size
static void *PyFloatToDOUBLE(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
{
PyObject *obj = (PyObject *) _obj;
*((double *) outValue) = PyFloat_AS_DOUBLE (obj);
*((double *) outValue) = PyFloat_AsDouble (obj);
return NULL;
}
@ -596,7 +602,7 @@ void Object_beginTypeContext (JSOBJ _obj, JSONTypeContext *tc)
return;
}
else
if (PyFloat_Check(obj))
if (PyFloat_Check(obj) || PyObject_IsInstance(obj, type_decimal))
{
PRINTMARK();
pc->PyTypeToJSON = PyFloatToDOUBLE; tc->type = JT_DOUBLE;
@ -804,7 +810,7 @@ char *Object_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "obj", "ensure_ascii", "double_precision", NULL};
static char *kwlist[] = { "obj", "ensure_ascii", "double_precision", "encode_html_chars", NULL};
char buffer[65536];
char *ret;
@ -812,6 +818,7 @@ PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
PyObject *oinput = NULL;
PyObject *oensureAscii = NULL;
int idoublePrecision = 10; // default double precision setting
PyObject *oencodeHTMLChars = NULL;
JSONObjectEncoder encoder =
{
@ -833,12 +840,13 @@ PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
-1, //recursionMax
idoublePrecision,
1, //forceAscii
0, //encodeHTMLChars
};
PRINTMARK();
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oi", kwlist, &oinput, &oensureAscii, &idoublePrecision))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OiO", kwlist, &oinput, &oensureAscii, &idoublePrecision, &oencodeHTMLChars))
{
return NULL;
}
@ -849,6 +857,11 @@ PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
encoder.forceASCII = 0;
}
if (oencodeHTMLChars != NULL && PyObject_IsTrue(oencodeHTMLChars))
{
encoder.encodeHTMLChars = 1;
}
encoder.doublePrecision = idoublePrecision;
PRINTMARK();

@ -52,12 +52,14 @@ PyObject* objToJSONFile(PyObject* self, PyObject *args, PyObject *kwargs);
PyObject* JSONFileToObj(PyObject* self, PyObject *args, PyObject *kwargs);
#define ENCODER_HELP_TEXT "Use ensure_ascii=false to output UTF-8. Pass in double_precision to alter the maximum digit precision of doubles. Set encode_html_chars=True to encode < > & as unicode escape sequences."
static PyMethodDef ujsonMethods[] = {
{"encode", (PyCFunction) objToJSON, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursivly into JSON. Use ensure_ascii=false to output UTF-8. Pass in double_precision to alter the maximum digit precision with doubles"},
{"encode", (PyCFunction) objToJSON, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursivly into JSON. " ENCODER_HELP_TEXT},
{"decode", (PyCFunction) JSONToObj, METH_VARARGS | METH_KEYWORDS, "Converts JSON as string to dict object structure. Use precise_float=True to use high precision float decoder."},
{"dumps", (PyCFunction) objToJSON, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursivly into JSON. Use ensure_ascii=false to output UTF-8"},
{"dumps", (PyCFunction) objToJSON, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursivly into JSON. " ENCODER_HELP_TEXT},
{"loads", (PyCFunction) JSONToObj, METH_VARARGS | METH_KEYWORDS, "Converts JSON as string to dict object structure. Use precise_float=True to use high precision float decoder."},
{"dump", (PyCFunction) objToJSONFile, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursively into JSON file. Use ensure_ascii=false to output UTF-8"},
{"dump", (PyCFunction) objToJSONFile, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursively into JSON file. " ENCODER_HELP_TEXT},
{"load", (PyCFunction) JSONFileToObj, METH_VARARGS | METH_KEYWORDS, "Converts JSON as file to dict object structure. Use precise_float=True to use high precision float decoder."},
{NULL, NULL, 0, NULL} /* Sentinel */
};

@ -35,4 +35,4 @@ http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
* Copyright (c) 1994 Sun Microsystems, Inc.
*/
#define UJSON_VERSION "1.29"
#define UJSON_VERSION "1.30"

@ -19,6 +19,7 @@ import calendar
import StringIO
import re
import random
import decimal
from functools import partial
PY3 = (sys.version_info[0] >= 3)
@ -32,6 +33,35 @@ json_unicode = (json.dumps if sys.version_info[0] >= 3
class UltraJSONTests(TestCase):
def test_encodeDecimal(self):
sut = decimal.Decimal("1337.1337")
encoded = ujson.encode(sut, double_precision=100)
decoded = ujson.decode(encoded)
self.assertEquals(decoded, 1337.1337)
def test_encodeStringConversion(self):
input = "A string \\ / \b \f \n \r \t </script> &"
not_html_encoded = '"A string \\\\ \\/ \\b \\f \\n \\r \\t <\\/script> &"'
html_encoded = '"A string \\\\ \\/ \\b \\f \\n \\r \\t \\u003c\\/script\\u003e \\u0026"'
def helper(expected_output, **encode_kwargs):
output = ujson.encode(input, **encode_kwargs)
self.assertEquals(input, json.loads(output))
self.assertEquals(output, expected_output)
self.assertEquals(input, ujson.decode(output))
# Default behavior assumes encode_html_chars=False.
helper(not_html_encoded, ensure_ascii=True)
helper(not_html_encoded, ensure_ascii=False)
# Make sure explicit encode_html_chars=False works.
helper(not_html_encoded, ensure_ascii=True, encode_html_chars=False)
helper(not_html_encoded, ensure_ascii=False, encode_html_chars=False)
# Make sure explicit encode_html_chars=True does the encoding.
helper(html_encoded, ensure_ascii=True, encode_html_chars=True)
helper(html_encoded, ensure_ascii=False, encode_html_chars=True)
def test_doubleLongIssue(self):
sut = {u'a': -4342969734183514}
encoded = json.dumps(sut)
@ -917,8 +947,8 @@ input = "someutfcharacters"
raise NotImplementedError("Implement this test!")
"""
if __name__ == "__main__":
unittest.main()
#if __name__ == "__main__":
# unittest.main()
# Use this to look for memory leaks