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:
parent
c5c3c7fbbc
commit
fd6cc28ab9
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user