Skip to content

Commit a11e143

Browse files
committed
Merge pull request #4976 from Komnomnomnom/json-locale-fix
FIX: JSON support for non C locales
2 parents 86f81b4 + edefe98 commit a11e143

File tree

4 files changed

+37
-1
lines changed

4 files changed

+37
-1
lines changed

doc/source/release.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,8 @@ Bug Fixes
358358
dtypes, surfaced in (:issue:`4377`)
359359
- Fixed bug with duplicate columns and type conversion in ``read_json`` when
360360
``orient='split'`` (:issue:`4377`)
361+
- Fixed JSON bug where locales with decimal separators other than '.' threw
362+
exceptions when encoding / decoding certain values. (:issue:`4918`)
361363
- Fix ``.iat`` indexing with a ``PeriodIndex`` (:issue:`4390`)
362364
- Fixed an issue where ``PeriodIndex`` joining with self was returning a new
363365
instance rather than the same instance (:issue:`4379`); also adds a test

pandas/io/tests/test_json/test_ujson.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,19 @@ def test_doubleLongDecimalIssue(self):
8383
decoded = ujson.decode(encoded)
8484
self.assertEqual(sut, decoded)
8585

86+
def test_encodeNonCLocale(self):
87+
import locale
88+
savedlocale = locale.getlocale(locale.LC_NUMERIC)
89+
try:
90+
locale.setlocale(locale.LC_NUMERIC, 'it_IT.UTF-8')
91+
except:
92+
try:
93+
locale.setlocale(locale.LC_NUMERIC, 'Italian_Italy')
94+
except:
95+
raise nose.SkipTest('Could not set locale for testing')
96+
self.assertEqual(ujson.loads(ujson.dumps(4.78e60)), 4.78e60)
97+
self.assertEqual(ujson.loads('4.78', precise_float=True), 4.78)
98+
locale.setlocale(locale.LC_NUMERIC, savedlocale)
8699

87100
def test_encodeDecodeLongDecimal(self):
88101
sut = {u('a'): -528656961.4399388}

pandas/src/ujson/lib/ultrajsondec.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Numeric decoder derived from from TCL library
4343
#include <wchar.h>
4444
#include <stdlib.h>
4545
#include <errno.h>
46+
#include <locale.h>
4647

4748
#ifndef TRUE
4849
#define TRUE 1
@@ -824,7 +825,7 @@ FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_object( struct DecoderState *ds)
824825

825826
default:
826827
ds->dec->releaseObject(ds->prv, newObj, ds->dec);
827-
return SetError(ds, -1, "Unexpected character in found when decoding object value");
828+
return SetError(ds, -1, "Unexpected character found when decoding object value");
828829
}
829830
}
830831
}
@@ -874,6 +875,7 @@ JSOBJ JSON_DecodeObject(JSONObjectDecoder *dec, const char *buffer, size_t cbBuf
874875
{
875876
/*
876877
FIXME: Base the size of escBuffer of that of cbBuffer so that the unicode escaping doesn't run into the wall each time */
878+
char *locale;
877879
struct DecoderState ds;
878880
wchar_t escBuffer[(JSON_MAX_STACK_BUFFER_SIZE / sizeof(wchar_t))];
879881
JSOBJ ret;
@@ -892,7 +894,15 @@ JSOBJ JSON_DecodeObject(JSONObjectDecoder *dec, const char *buffer, size_t cbBuf
892894

893895
ds.dec = dec;
894896

897+
locale = strdup(setlocale(LC_NUMERIC, NULL));
898+
if (!locale)
899+
{
900+
return SetError(&ds, -1, "Could not reserve memory block");
901+
}
902+
setlocale(LC_NUMERIC, "C");
895903
ret = decode_any (&ds);
904+
setlocale(LC_NUMERIC, locale);
905+
free(locale);
896906

897907
if (ds.escHeap)
898908
{

pandas/src/ujson/lib/ultrajsonenc.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Numeric decoder derived from from TCL library
4141
#include <string.h>
4242
#include <stdlib.h>
4343
#include <math.h>
44+
#include <locale.h>
4445

4546
#include <float.h>
4647

@@ -877,6 +878,7 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName)
877878

878879
char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *enc, char *_buffer, size_t _cbBuffer)
879880
{
881+
char *locale;
880882
enc->malloc = enc->malloc ? enc->malloc : malloc;
881883
enc->free = enc->free ? enc->free : free;
882884
enc->realloc = enc->realloc ? enc->realloc : realloc;
@@ -915,7 +917,16 @@ char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *enc, char *_buffer, size_t
915917
enc->end = enc->start + _cbBuffer;
916918
enc->offset = enc->start;
917919

920+
locale = strdup(setlocale(LC_NUMERIC, NULL));
921+
if (!locale)
922+
{
923+
SetError(NULL, enc, "Could not reserve memory block");
924+
return NULL;
925+
}
926+
setlocale(LC_NUMERIC, "C");
918927
encode (obj, enc, NULL, 0);
928+
setlocale(LC_NUMERIC, locale);
929+
free(locale);
919930

920931
Buffer_Reserve(enc, 1);
921932
if (enc->errorMsg)

0 commit comments

Comments
 (0)