如何从JSON获取string对象而不是Unicode?

我使用Python 2ASCII编码的文本文件中parsingJSON。

当用jsonsimplejson加载这些文件时,我所有的string值都被转换为Unicode对象而不是string对象。 问题是,我必须使用一些只接受string对象的库的数据。 我不能更改库或更新它们。

是否有可能获得string对象而不是Unicode的?

 >>> import json >>> original_list = ['a', 'b'] >>> json_list = json.dumps(original_list) >>> json_list '["a", "b"]' >>> new_list = json.loads(json_list) >>> new_list [u'a', u'b'] # I want these to be of type `str`, not `unicode` 

更新

很久以前,当我被Python 2困住时,这个问题被问到 。 今天一个简单而干净的解决scheme就是使用Python的最新版本 – 即Python 3和forward。

一个解决scheme与object_hook

 import json def json_load_byteified(file_handle): return _byteify( json.load(file_handle, object_hook=_byteify), ignore_dicts=True ) def json_loads_byteified(json_text): return _byteify( json.loads(json_text, object_hook=_byteify), ignore_dicts=True ) def _byteify(data, ignore_dicts = False): # if this is a unicode string, return its string representation if isinstance(data, unicode): return data.encode('utf-8') # if this is a list of values, return list of byteified values if isinstance(data, list): return [ _byteify(item, ignore_dicts=True) for item in data ] # if this is a dictionary, return dictionary of byteified keys and values # but only if we haven't already byteified it if isinstance(data, dict) and not ignore_dicts: return { _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() } # if it's anything else, return it in its original form return data 

用法示例:

 >>> json_loads_byteified('{"Hello": "World"}') {'Hello': 'World'} >>> json_loads_byteified('"I am a top-level string"') 'I am a top-level string' >>> json_loads_byteified('7') 7 >>> json_loads_byteified('["I am inside a list"]') ['I am inside a list'] >>> json_loads_byteified('[[[[[[[["I am inside a big nest of lists"]]]]]]]]') [[[[[[[['I am inside a big nest of lists']]]]]]]] >>> json_loads_byteified('{"foo": "bar", "things": [7, {"qux": "baz", "moo": {"cow": ["milk"]}}]}') {'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'} >>> json_load_byteified(open('somefile.json')) {'more json': 'from a file'} 

这是如何工作,为什么我会使用它?

Mark Amery的function比这些更短,更清晰,所以他们有什么意义呢? 你为什么要使用它们?

纯粹是为了performance 。 Mark的答案完全用unicodestring解码JSON文本,然后遍历整个解码值,将所有string转换为字节string。 这有一些不良影响:

  • 整个解码结构的副本在内存中创build
  • 如果你的JSON对象真的深度嵌套(500级或更多),那么你会打到Python的最大recursion深度

这个答案通过使用json.loadjson.loadsobject_hook参数来缓解这两个性能问题。 从文档 :

object_hook是一个可选的函数,将被调用的任何对象字面解码的结果(一个dict )。 object_hook的返回值将被用来代替dict 。 这个function可以用来实现自定义解码器

由于字典嵌套在其他字典深层次的许多层次,所以在解码的时候会传递给object_hook ,所以我们可以在这个字节中对string或列表进行字节化,避免以后需要深度recursion。

Mark的回答并不适合作为一个object_hook ,因为它recursion到嵌套字典中。 我们使用_byteifyignore_dicts参数来阻止这个回答的recursion, 除非 object_hook传递一个新的dict来进行字节化, _byteify它会一直传递给它。 ignore_dicts标志告诉_byteify忽略dict因为它们已经被字节化了。

最后,我们对json_load_byteifiedjson_loads_byteified实现对json_load_byteifiedjson.loads返回的结果调用_byteify (with ignore_dicts=True ),以处理被解码的JSON文本在顶层没有dict情况。

虽然这里有一些很好的答案,但是我最终使用PyYAMLparsing了我的JSON文件,因为它将键和值作为strtypesstring而不是unicodetypes。 因为JSON是YAML的一个子集,所以它很好地工作:

 >>> 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'] 

笔记

有些事情要注意,但:

  • 我得到string对象,因为我所有的条目都是ASCII编码的 。 如果我使用unicode编码的条目,我会把它们作为unicode对象返回 – 没有转换!

  • 你应该(可能总是)使用PyYAML的safe_load函数; 如果使用它来加载JSON文件,则不需要load函数的“附加function”。

  • 如果你想要一个支持1.2版规范的YAMLparsing器(并且正确parsing非常低的数字 ),请尝试Ruamel YAML : pip install ruamel.yamlimport ruamel.yaml as yaml是我testing中需要的。

转变

如上所述,没有转换! 如果你不能确定只处理ASCII值(大多数时候你不能确定),最好使用转换函数

我曾经使用过Mark Amery的一个,它工作的非常好,使用起来非常简单。 你也可以使用一个类似于object_hook函数,因为它可能会使大文件的性能提升。 请参阅Mirec Miskuf为此提供的稍微更复杂的答案 。

有没有内置的选项,使json模块函数返回字节string,而不是unicodestring。 但是,这个简短的recursion函数会将任何已解码的JSON对象从使用unicodestring转换为UTF-8编码的字节string:

 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 

只要在从json.loadjson.loads调用中获得的输出上调用它即可。

几个注意事项:

  • 要支持Python 2.6或更早版本,请使用return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])replacereturn {byteify(key): byteify(value) for key, value in input.iteritems()} return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]) ,因为字典return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])不支持,直到Python 2.7。
  • 由于这个答案在整个解码对象中recursion,所以它有一些不良的性能特征,可以通过非常小心地使用object_hookobject_pairs_hook参数来避免。 Mirec Miskuf的答案迄今为止是唯一一个能够正确解决这个问题的方法,尽pipe如此,这比我的方法要复杂得多。

您可以使用object_hook参数传入转换器。 事实之后,您不必进行转换。 json模块总是只传递object_hook字典,并且会recursion地传递嵌套的字典,所以你不必自己recursion嵌套的字典。 我不认为我会把Unicodestring转换成像Wells节目那样的数字。 如果它是一个unicodestring,它被引用为JSON文件中的一个string,所以它应该是一个string(或文件是坏的)。

另外,我会尽量避免在unicode对象上做类似str(val)事情。 你应该使用value.encode(encoding)和一个有效的编码,这取决于你的外部库期望的。

所以,例如:

 def _decode_list(data): rv = [] for item in data: if isinstance(item, unicode): item = item.encode('utf-8') elif isinstance(item, list): item = _decode_list(item) elif isinstance(item, dict): item = _decode_dict(item) rv.append(item) return rv def _decode_dict(data): rv = {} for key, value in data.iteritems(): if isinstance(key, unicode): key = key.encode('utf-8') if isinstance(value, unicode): value = value.encode('utf-8') elif isinstance(value, list): value = _decode_list(value) elif isinstance(value, dict): value = _decode_dict(value) rv[key] = value return rv obj = json.loads(s, object_hook=_decode_dict) 

这是因为json在string对象和unicode对象之间没有区别。 他们都在JavaScript中的string。

我认为JSON是正确的返回unicode对象 。 事实上,我不会接受任何东西,因为JavaScriptstring实际上是unicode对象 (即JSON(javascript)string可以存储任何种类的Unicode字符),所以当从JSON转换string时创buildunicode对象是有意义的。 简单的string不适合,因为库将不得不猜测你想要的编码。

在任何地方最好使用unicodestring对象。 所以你最好的select是更新你的库,以便他们可以处理unicode对象。

但是,如果您真的想要字节串,只需将结果编码为您select的编码:

 >>> nl = json.loads(js) >>> nl [u'a', u'b'] >>> nl = [s.encode('utf-8') for s in nl] >>> nl ['a', 'b'] 

有一个简单的解决方法。

TL; DR – 使用ast.literal_eval()而不是json.loads()astjson都在标准库中。

虽然不是一个“完美”的答案,但是如果你的计划完全忽略Unicode的话,那么这个答案就相当的遥远了。 在Python 2.7

 import json, ast d = { 'field' : 'value' } print "JSON Fail: ", json.loads(json.dumps(d)) print "AST Win:", ast.literal_eval(json.dumps(d)) 

得到:

 JSON Fail: {u'field': u'value'} AST Win: {'field': 'value'} 

当一些对象真的是Unicodestring时,这会变得更加多毛。 完整的答案迅速变得毛茸茸的。

恐怕没有办法在simplejson库中自动实现这一点。

simplejson中的扫描器和解码器用于生成unicode文本。 要做到这一点,库使用一个称为c_scanstring (如果可用,为了速度),或者py_scanstring如果C版本不可用的函数。 几乎每个例程都会调用scanstring函数,simplejson用于解码可能包含文本的结构。 你必须在simplejson.decoder或者JSONDecoder子类中JSONDecoder scanstring值,并且提供几乎所有可能包含文本的实现。

然而,simplejson输出unicode的原因是, json规范特别提到“string是零个或多个Unicode字符的集合”…支持unicode被认为是格式本身的一部分。 Simplejson的扫描scanstring实现甚至扫描和解释unicode转义(甚至错误检查畸形的多字节字符集表示),所以唯一可以将值可靠地返回给你的方式是unicode。

如果你有一个老旧的库需要一个str ,我build议你在parsing之后费力地search嵌套的数据结构(我承认你明确表示你想避免…抱歉),或者可能用某种方式来包装你的库您可以在更精细的层次上按摩input参数。 第二种方法可能比第一种方法更容易pipe理,如果你的数据结构确实是深度嵌套的。

麦克·布伦南的答案很接近,但没有理由重新遍历整个结构。 如果您使用object_hook_pairs (Python 2.7+)参数:

object_pairs_hook是一个可选函数,将被调用的任何对象文本的结果与有序列表对解码。 将使用object_pairs_hook的返回值来代替dict 。 此function可用于实现自定义解码器,这些解码器依赖于解码键和值对的顺序(例如collections.OrderedDict会记住插入的顺序)。 如果还定义了object_hook ,则object_pairs_hook优先。

有了它,你可以得到每个JSON对象交给你,所以你可以做解码,不需要recursion:

 def deunicodify_hook(pairs): new_pairs = [] for key, value in pairs: if isinstance(value, unicode): value = value.encode('utf-8') if isinstance(key, unicode): key = key.encode('utf-8') new_pairs.append((key, value)) return dict(new_pairs) In [52]: open('test.json').read() Out[52]: '{"1": "hello", "abc": [1, 2, 3], "def": {"hi": "mom"}, "boo": [1, "hi", "moo", {"5": "some"}]}' In [53]: json.load(open('test.json')) Out[53]: {u'1': u'hello', u'abc': [1, 2, 3], u'boo': [1, u'hi', u'moo', {u'5': u'some'}], u'def': {u'hi': u'mom'}} In [54]: json.load(open('test.json'), object_pairs_hook=deunicodify_hook) Out[54]: {'1': 'hello', 'abc': [1, 2, 3], 'boo': [1, 'hi', 'moo', {'5': 'some'}], 'def': {'hi': 'mom'}} 

请注意,我不需要recursion调用钩子,因为在使用object_pairs_hook时,每个对象都会被挂钩。 你必须关心列表,但正如你所看到的,列表中的一个对象将被正确地转换,而不需要recursion来实现。

编辑:一位同事指出,Python2.6没有object_hook_pairs 。 你仍然可以通过做一个非常小的改变来使用Python2.6。 在上面的钩子中,更改:

 for key, value in pairs: 

 for key, value in pairs.iteritems(): 

然后使用object_hook而不是object_pairs_hook

 In [66]: json.load(open('test.json'), object_hook=deunicodify_hook) Out[66]: {'1': 'hello', 'abc': [1, 2, 3], 'boo': [1, 'hi', 'moo', {'5': 'some'}], 'def': {'hi': 'mom'}} 

使用object_pairs_hook导致为JSON对象中的每个对象实例化一个较less的字典,如果parsing一个巨大的文档,这可能是值得的。

简单jsonjson是两个不同的模块,至less在处理unicode方面是这样的。 你有py2.6 + json ,这给你unicode值,而simplejson返回string对象。 只要在您的环境中尝试easy_install-simplejson,看看是否有效。 它为我做了。

正如Mark(Amery)正确地注意到:在json转储中使用PyYaml的反序列化器只有在只有ASCII的情况下才有效。 至less开箱即可

关于PyYaml方法的两个快速评论:

  1. 不要在字段中使用yaml.load。 它是yaml的一个特性(!)来执行隐藏在结构中的任意代码。

  2. 可以通过以下方法使它对非ASCII码也起作用:

     def to_utf8(loader, node): return loader.construct_scalar(node).encode('utf-8') yaml.add_constructor(u'tag:yaml.org,2002:str', to_utf8) 

但是,与马克·阿梅里(Mark Amery)的回答无异:

将一些深度嵌套的示例字典扔到两个方法上,我得到这个(dt [j] = json.loads(json.dumps(m))的时间增量):

  dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j] dt[byteify recursion(Mark Amery)] =~ 5 * dt[j] 

所以反序列化包括完全遍历树编码,完全在json基于C的实现的数量级内。 我发现这个速度非常快,而且比深层嵌套结构的Yaml载荷更加稳健。 而且安全性较低,容易出错,看着yaml.load。

=>虽然我会欣赏一个指向基于C的转换器的指针,但是byteify函数应该是默认的答案。

如果您的json结构来自包含用户input的字段,则这尤其适用。 因为那么你可能需要在你的结构上走动 – 独立于你想要的内部数据结构(只有'unicode sandwich'或字节string)。

为什么?

Unicode 规范化 。 为了不知道:拿一个止痛药,并阅读。

所以使用字节recursion,你可以一箭双雕:

  1. 从嵌套的json转储中获取你的字节串
  2. 获取用户input值标准化,以便您find存储中的东西。

在我的testing中,事实certificate,用unicodedata.normalize('NFC',input).encode('utf-8')replaceinput.encode('utf-8')比无NFC这很大程度上依赖于我猜测的样本数据。

所以,我遇到了同样的问题。 猜猜Google的第一个结果是什么?

因为我需要将所有数据传递给PyGTK,所以unicodestring对我来说也不是很有用。 所以我有另一个recursion转换方法。 它实际上还需要进行types安全的JSON转换 – json.dump()将保留在任何非文字(如Python对象)上。 虽然不转换字典索引。

 # removes any objects, turns unicode back into str def filter_data(obj): if type(obj) in (int, float, str, bool): return obj elif type(obj) == unicode: return str(obj) elif type(obj) in (list, tuple, set): obj = list(obj) for i,v in enumerate(obj): obj[i] = filter_data(v) elif type(obj) == dict: for i,v in obj.iteritems(): obj[i] = filter_data(v) else: print "invalid object in data, converting to string" obj = str(obj) return obj 

只需使用pickle而不是json来转储和加载,如下所示:

  import json import pickle d = { 'field1': 'value1', 'field2': 2, } json.dump(d,open("testjson.txt","w")) print json.load(open("testjson.txt","r")) pickle.dump(d,open("testpickle.txt","w")) print pickle.load(open("testpickle.txt","r")) 

它产生的输出是(string和整数正确处理):

  {u'field2': 2, u'field1': u'value1'} {'field2': 2, 'field1': 'value1'} 

看看这个类似的问题,这个说明这个答案

u-前缀意味着你有一个Unicodestring。 当你真的使用这个string时,它不会出现在你的数据中。 不要被打印输出。

例如,试试这个:

 print mail_accounts[0]["i"] 

你不会看到你的。

使用hook支持Python2&3(从https://stackoverflow.com/a/33571117/558397

 import requests import six from six import iteritems requests.packages.urllib3.disable_warnings() # @UndefinedVariable r = requests.get("http://echo.jsontest.com/key/value/one/two/three", verify=False) def _byteify(data): # if this is a unicode string, return its string representation if isinstance(data, six.string_types): return str(data.encode('utf-8').decode()) # if this is a list of values, return list of byteified values if isinstance(data, list): return [ _byteify(item) for item in data ] # if this is a dictionary, return dictionary of byteified keys and values # but only if we haven't already byteified it if isinstance(data, dict): return { _byteify(key): _byteify(value) for key, value in iteritems(data) } # if it's anything else, return it in its original form return data w = r.json(object_hook=_byteify) print(w) 

返回:

  {'three': '', 'key': 'value', 'one': 'two'} 

这是迟到的游戏,但我build立了这个recursion施法者。 它适合我的需求,我认为它是相对完整的。 它可以帮助你。

 def _parseJSON(self, obj): newobj = {} for key, value in obj.iteritems(): key = str(key) if isinstance(value, dict): newobj[key] = self._parseJSON(value) elif isinstance(value, list): if key not in newobj: newobj[key] = [] for i in value: newobj[key].append(self._parseJSON(i)) elif isinstance(value, unicode): val = str(value) if val.isdigit(): val = int(val) else: try: val = float(val) except ValueError: val = str(val) newobj[key] = val return newobj 

只要传递一个JSON对象就可以了:

 obj = json.loads(content, parse_float=float, parse_int=int) obj = _parseJSON(obj) 

我把它作为一个类的私人成员,但是你可以根据你的需要重新调整方法。

我重写了Wells的_parse_json()来处理json对象本身是一个数组(我的用例)的情况。

 def _parseJSON(self, obj): if isinstance(obj, dict): newobj = {} for key, value in obj.iteritems(): key = str(key) newobj[key] = self._parseJSON(value) elif isinstance(obj, list): newobj = [] for value in obj: newobj.append(self._parseJSON(value)) elif isinstance(obj, unicode): newobj = str(obj) else: newobj = obj return newobj 

here is a recursive encoder written in C: https://github.com/axiros/nested_encode

Performance overhead for "average" structures around 10% compared to json.loads.

 python speed.py json loads [0.16sec]: {u'a': [{u'b': [[1, 2, [u'\xd6ster.. json loads + encoding [0.18sec]: {'a': [{'b': [[1, 2, ['\xc3\x96ster. time overhead in percent: 9% 

using this teststructure:

 import json, nested_encode, time s = """ { "firstName": "Jos\\u0301", "lastName": "Smith", "isAlive": true, "age": 25, "address": { "streetAddress": "21 2nd Street", "city": "\\u00d6sterreich", "state": "NY", "postalCode": "10021-3100" }, "phoneNumbers": [ { "type": "home", "number": "212 555-1234" }, { "type": "office", "number": "646 555-4567" } ], "children": [], "spouse": null, "a": [{"b": [[1, 2, ["\\u00d6sterreich"]]]}] } """ t1 = time.time() for i in xrange(10000): u = json.loads(s) dt_json = time.time() - t1 t1 = time.time() for i in xrange(10000): b = nested_encode.encode_nested(json.loads(s)) dt_json_enc = time.time() - t1 print "json loads [%.2fsec]: %s..." % (dt_json, str(u)[:20]) print "json loads + encoding [%.2fsec]: %s..." % (dt_json_enc, str(b)[:20]) print "time overhead in percent: %i%%" % (100 * (dt_json_enc - dt_json)/dt_json) 

I ran into this problem too, and having to deal with JSON, I came up with a small loop that converts the unicode keys to strings. ( simplejson on GAE does not return string keys.)

obj is the object decoded from JSON:

 if NAME_CLASS_MAP.has_key(cls): kwargs = {} for i in obj.keys(): kwargs[str(i)] = obj[i] o = NAME_CLASS_MAP[cls](**kwargs) o.save() 

kwargs is what I pass to the constructor of the GAE application (which does not like unicode keys in **kwargs )

Not as robust as the solution from Wells, but much smaller.

I've adapted the code from the answer of Mark Amery , particularly in order to get rid of isinstance for the pros of duck-typing.

The encoding is done manually and ensure_ascii is disabled. The python docs for json.dump says that

If ensure_ascii is True (the default), all non-ASCII characters in the output are escaped with \uXXXX sequences

Disclaimer: in the doctest I used the Hungarian language. Some notable Hungarian-related character encodings are: cp852 the IBM/OEM encoding used eg. in DOS (sometimes referred as ascii , incorrectly I think, it is dependent on the codepage setting), cp1250 used eg. in Windows (sometimes referred as ansi , dependent on the locale settings), and iso-8859-2 , sometimes used on http servers. The test text Tüskéshátú kígyóbűvölő is attributed to Koltai László (native personal name form) and is from wikipedia .

 # coding: utf-8 """ This file should be encoded correctly with utf-8. """ import json def encode_items(input, encoding='utf-8'): u"""original from: https://stackoverflow.com/a/13101776/611007 adapted by SO/u/611007 (20150623) >>> >>> ## run this with `python -m doctest <this file>.py` from command line >>> >>> txt = u"Tüskéshátú kígyóbűvölő" >>> txt2 = u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151" >>> txt3 = u"uúuutifu" >>> txt4 = b'u\\xfauutifu' >>> # txt4 shouldn't be 'u\\xc3\\xbauutifu', string content needs double backslash for doctest: >>> assert u'\\u0102' not in b'u\\xfauutifu'.decode('cp1250') >>> txt4u = txt4.decode('cp1250') >>> assert txt4u == u'u\\xfauutifu', repr(txt4u) >>> txt5 = b"u\\xc3\\xbauutifu" >>> txt5u = txt5.decode('utf-8') >>> txt6 = u"u\\u251c\\u2551uutifu" >>> there_and_back_again = lambda t: encode_items(t, encoding='utf-8').decode('utf-8') >>> assert txt == there_and_back_again(txt) >>> assert txt == there_and_back_again(txt2) >>> assert txt3 == there_and_back_again(txt3) >>> assert txt3.encode('cp852') == there_and_back_again(txt4u).encode('cp852') >>> assert txt3 == txt4u,(txt3,txt4u) >>> assert txt3 == there_and_back_again(txt5) >>> assert txt3 == there_and_back_again(txt5u) >>> assert txt3 == there_and_back_again(txt4u) >>> assert txt3.encode('cp1250') == encode_items(txt4, encoding='utf-8') >>> assert txt3.encode('utf-8') == encode_items(txt5, encoding='utf-8') >>> assert txt2.encode('utf-8') == encode_items(txt, encoding='utf-8') >>> assert {'a':txt2.encode('utf-8')} == encode_items({'a':txt}, encoding='utf-8') >>> assert [txt2.encode('utf-8')] == encode_items([txt], encoding='utf-8') >>> assert [[txt2.encode('utf-8')]] == encode_items([[txt]], encoding='utf-8') >>> assert [{'a':txt2.encode('utf-8')}] == encode_items([{'a':txt}], encoding='utf-8') >>> assert {'b':{'a':txt2.encode('utf-8')}} == encode_items({'b':{'a':txt}}, encoding='utf-8') """ try: input.iteritems return {encode_items(k): encode_items(v) for (k,v) in input.iteritems()} except AttributeError: if isinstance(input, unicode): return input.encode(encoding) elif isinstance(input, str): return input try: iter(input) return [encode_items(e) for e in input] except TypeError: return input def alt_dumps(obj, **kwargs): """ >>> alt_dumps({'a': u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"}) '{"a": "T\\xc3\\xbcsk\\xc3\\xa9sh\\xc3\\xa1t\\xc3\\xba k\\xc3\\xadgy\\xc3\\xb3b\\xc5\\xb1v\\xc3\\xb6l\\xc5\\x91"}' """ if 'ensure_ascii' in kwargs: del kwargs['ensure_ascii'] return json.dumps(encode_items(obj), ensure_ascii=False, **kwargs) 

I'd also like to highlight the answer of Jarret Hardie which references the JSON spec , quoting:

A string is a collection of zero or more Unicode characters

In my use-case I had files with json. They are utf-8 encoded files. ensure_ascii results in properly escaped but not very readable json files, that is why I've adapted Mark Amery's answer to fit my needs.

The doctest is not particularly thoughtful but I share the code in the hope that it will useful for someone.

I had a JSON dict as a string. The keys and values were unicode objects like in the following example:

 myStringDict = "{u'key':u'value'}" 

I could use the byteify function suggested above by converting the string to a dict object using ast.literal_eval(myStringDict) .