我使用Python 2从ASCII编码的文本文件解析JSON。

当用json或simplejson加载这些文件时,我的所有字符串值都转换为Unicode对象而不是字符串对象。问题是,我必须将数据与一些只接受字符串对象的库一起使用。我不能更改库也不能更新它们。

是否有可能获得字符串对象而不是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`

(2017年一个简单而干净的解决方案是使用最新版本的Python——即Python 3和更高版本。)


当前回答

我有一个JSON字典作为字符串。键和值是Unicode对象,如下例所示:

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

我可以使用上面建议的byteify函数,使用ast.literal_eval(myStringDict)将字符串转换为dict对象。

其他回答

我也遇到了同样的问题。

因为我需要将所有数据传递给PyGTK,所以Unicode字符串对我来说也不是很有用。这是另一种递归转换方法。实际上,类型安全的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

这是因为json()在字符串对象和Unicode对象之间没有区别。它们都是JavaScript中的字符串。

我认为JSON返回Unicode对象是正确的。事实上,我不会接受更少的东西,因为JavaScript字符串实际上是unicode对象(即JSON (JavaScript)字符串可以存储任何类型的unicode字符),因此在从JSON转换字符串时创建unicode对象是有意义的。普通字符串不适合,因为库必须猜测您想要的编码。

最好在任何地方都使用unicode字符串对象。因此,最好的选择是更新库,使它们能够处理Unicode对象。

但如果你真的想要字节串,只需将结果编码为你选择的编码:

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

Mark (Amery)正确地指出:在JSON转储上使用PyYAML的反序列化器仅在只有ASCII时有效。至少是开箱即用。

关于PyYAML方法的两个简短评论:

永远不要对来自字段的数据使用yaml.load()。这是YAML的一个特性(!),可以执行隐藏在结构中的任意代码。 你也可以通过以下方法使它适用于非ASCII: Def to_utf8(加载器,节点): 返回loader.construct_scalar(节点).encode(“utf - 8”) yaml.add_constructor (u 'tag: yaml.org, 2002: str ', to_utf8)

但就性能而言,这与马克·艾默里的答案无法相提并论:

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

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

因此,反序列化(包括完全遍历树和编码)完全在基于c语言的JSON实现的数量级之内。我发现这非常快,而且在深度嵌套结构上比yaml加载更健壮。更少的安全错误,看yaml.load。

虽然我很喜欢一个指向c语言的转换器的指针,但byteify函数应该是默认答案。

如果JSON结构来自包含用户输入的字段,则尤其如此。因为这样你可能需要遍历你的结构——独立于你想要的内部数据结构(“unicode三明治”或字节字符串)。

Why?

Unicode正常化。给不知情的人:吃片止痛药,看看这篇文章。

所以使用byteify递归你一石二鸟:

从嵌套的JSON转储中获取字节串 让用户输入值正常化,这样你就可以在你的存储中找到东西。

在我的测试中,结果是将input.encode('utf-8')替换为unicodedata。normalize('NFC', input).encode('utf-8')甚至比没有NFC时还要快——但我猜这在很大程度上依赖于样本数据。

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

前缀u表示你有一个Unicode字符串。当你真正使用字符串时,它不会出现在你的数据中。不要被打印出来的结果所迷惑。

例如,试试这个:

print mail_accounts[0]["i"]

你不会看到u。

只需使用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"))

它产生的输出是(字符串和整数被正确处理):

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