Just decode the JSON as JSON, and then write out a new JSON document without ensuring the data is ASCII safe:
import json
with open("input.json", "r", encoding="utf-8") as input:
with open("output.txt", "w", encoding="utf-8") as output:
document = json.load(input)
json.dump(document, output, ensure_ascii=False)
From the json.dump() documentation:
If ensure_ascii is true (the default), the output is guaranteed to have all incoming non-ASCII characters escaped. If ensure_ascii is false, these characters will be output as-is.
Demo:
>>> import json
>>> print(json.loads(r'"l\u00f6yt\u00e4\u00e4"'))
löytää
>>> print(json.dumps(json.loads(r'"l\u00f6yt\u00e4\u00e4"')))
"l\u00f6yt\u00e4\u00e4"
>>> print(json.dumps(json.loads(r'"l\u00f6yt\u00e4\u00e4"'), ensure_ascii=False))
"löytää"
If you have extremely large documents, you could still process them textually, line by line, but use regular expressions to do the replacements:
import re
unicode_escape = re.compile(
r'(?<!\\)'
r'(?:\\u([dD][89abAB][a-fA-F0-9]{2})\\u([dD][c-fC-F][a-fA-F0-9]{2})'
r'|\\u([a-fA-F0-9]{4}))')
def replace(m):
return bytes.fromhex(''.join(m.groups(''))).decode('utf-16-be')
with open("input.json", "r", encoding="utf-8") as input:
with open("output.txt", "w", encoding="utf-8") as output:
for line in input:
output.write(unicode_escape.sub(replace, line))
This however fails if your JSON has embedded JSON documents in strings or if the escape sequence is preceded by an escaped backslash.
Answer from Martijn Pieters on Stack Overflowpython - how to find and replace unicode in json files? - Stack Overflow
Replace escaped Unicode chars (`\u20ac`) in stored JSON?
JSON unicode characters conversion - javascript
python - How to get string objects instead of Unicode from JSON - Stack Overflow
While there are some good answers here, I ended up using PyYAML to parse my JSON files, since it gives the keys and values as str type strings instead of the unicode type. Because JSON is a subset of YAML, it works nicely:
>>> import json
>>> import yaml
>>> list_org = ['a', 'b']
>>> list_dump = json.dumps(list_org)
>>> list_dump
'["a", "b"]'
>>> json.loads(list_dump)
[u'a', u'b']
>>> yaml.safe_load(list_dump)
['a', 'b']
Notes
Some things to note though:
I get string objects because all my entries are ASCII encoded. If I would use Unicode encoded entries, I would get them back as unicode objects — there is no conversion!
You should (probably always) use PyYAML's
safe_loadfunction; if you use it to load JSON files, you don't need the "additional power" of theloadfunction anyway.If you want a YAML parser that has more support for the 1.2 version of the spec (and correctly parses very low numbers) try Ruamel YAML:
pip install ruamel.yamlandimport ruamel.yaml as yamlwas all I needed in my tests.
Conversion
As stated, there isn't any conversion! If you can't be sure to only deal with ASCII values (and you can't be sure most of the time), better use a conversion function:
I used the one from Mark Amery a couple of times now, it works great and is very easy to use. You can also use a similar function as an object_hook instead, as it might gain you a performance boost on big files. See the slightly more involved answer from Mirec Miskuf for that.
There's no built-in option to make the json module functions return byte strings instead of Unicode strings. However, this short and simple recursive function will convert any decoded JSON object from using Unicode strings to UTF-8-encoded byte strings:
def byteify(input):
if isinstance(input, dict):
return {byteify(key): byteify(value)
for key, value in input.iteritems()}
elif isinstance(input, list):
return [byteify(element) for element in input]
elif isinstance(input, unicode):
return input.encode('utf-8')
else:
return input
Just call this on the output you get from a json.load or json.loads call.
A couple of notes:
- To support Python 2.6 or earlier, replace
return {byteify(key): byteify(value) for key, value in input.iteritems()}withreturn dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]), since dictionary comprehensions weren't supported until Python 2.7. - Since this answer recurses through the entire decoded object, it has a couple of undesirable performance characteristics that can be avoided with very careful use of the
object_hookorobject_pairs_hookparameters. Mirec Miskuf's answer is so far the only one that manages to pull this off correctly, although as a consequence, it's significantly more complicated than my approach.
This is not a bug in either implementation. There is no requirement to escape U+00B0. To quote the RFC:
2.5. Strings
The representation of strings is similar to conventions used in the C family of programming languages. A string begins and ends with quotation marks. All Unicode characters may be placed within the quotation marks except for the characters that must be escaped: quotation mark, reverse solidus, and the control characters (U+0000 through U+001F).
Any character may be escaped.
Escaping everything inflates the size of the data (all code points can be represented in four or fewer bytes in all Unicode transformation formats; whereas encoding them all makes them six or twelve bytes).
It is more likely that you have a text transcoding bug somewhere in your code and escaping everything in the ASCII subset masks the problem. It is a requirement of the JSON spec that all data use a Unicode encoding.
hmm, well here's a workaround anyway:
function JSON_stringify(s, emit_unicode)
{
var json = JSON.stringify(s);
return emit_unicode ? json : json.replace(/[\u007f-\uffff]/g,
function(c) {
return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4);
}
);
}
test case:
js>s='15\u00f8C 3\u0111';
15°C 3◄
js>JSON_stringify(s, true)
"15°C 3◄"
js>JSON_stringify(s, false)
"15\u00f8C 3\u0111"
According to the PHP documentation on stripslashes (), it
un-quotes a quoted string.
Which means, that it basically removes all backslashes, which are used for escaping characters (or Unicode sequences). When removing those, you basically have no chance to be completely sure that any sequence as "u0024" was meant to be a Unicode entity, your user could just have entered that.
Besides that, you will get some trouble when using stripslashes () on a JSON value that contains escaped quotes. Consider this example:
{
"key": "\"value\""
}
This will become invalid when using stripslashes () because it will then look like this:
{
"key": ""value""
}
Which is not parseable as it isn't a valid JSON object. When you don't use stripslashes (), all escape sequences will be converted by the JSON parser and before outputting the (decoded) JSON object to the client, PHP will automatically decode (or "convert") the Unicode sequences your data may contain.
Conclusion: I'd suggest not to use stripslashes () when dealing with JSON entities as it may break things (as seen in the previous example, but also in your problem).
The main question you have to understand, is why do you need to strip slashes? And, if it is really necessary to strip slashes, how to manage the encoding? Probably it is a good idea to convert unicode symbols before to strip slashes, not after, using html_entity_decode .
Anyway, you can try fix the problem with this workaround:
$string = "Hello World, I have u00249999999 dollars";
$string = preg_replace( "/u([0-9A-F]{0,4})/", "&#x$1;", $string ); // recover "u" + 4 alnums
$string = html_entity_decode( $string, ENT_COMPAT, 'UTF-8' ); // convert to utf-8
This snippet will helps you to preserve the data without unicode prefix notation u :
>>> import json
>>> c = {u'xyz': {u'key1': u'Value1',u'key2': u'Value2'}}
>>> print c
{u'xyz': {u'key2': u'Value2', u'key1': u'Value1'}}
>>> d = eval(json.dumps(c))
>>> print d
{'xyz': {'key2': 'Value2', 'key1': 'Value1'}}
json.dumps() will convert the dict to string type and eval() will reverse it.
Note: key1 value has changed from None to 'value1' for testing purpose
Change your None to 'None':
c = {u'xyz': {u'key1': 'None', u'key2': u'Value2'}}
it is a casting issue - ast likes str's
Also, maybe u want to change all None to empty str or 'None' str... See this thread : Python: most idiomatic way to convert None to empty string? with this code, i've changes the empty string to 'None':
def xstr(s):
if s is None:
return 'None'
return str(s)