Estou usando o Python 2 para analisar JSON de arquivos de texto codificados em ASCII .
Ao carregar esses arquivos com json
ou simplejson
, todos os meus valores de sequência são convertidos em objetos Unicode em vez de objetos de sequência. O problema é que eu tenho que usar os dados com algumas bibliotecas que aceitam apenas objetos de string. Não consigo alterar as bibliotecas nem atualizá-las.
É possível obter objetos de string em vez de Unicode?
Exemplo
>>> 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`
Atualizar
Esta pergunta foi feita há muito tempo , quando eu estava preso no Python 2 . Uma solução fácil e limpa para hoje é usar uma versão recente do Python - ie Python 3 e para a frente.
python
json
serialization
unicode
python-2.x
Brutus
fonte
fonte
str
Respostas:
Uma solução com
object_hook
Exemplo de uso:
Como isso funciona e por que eu o usaria?
A função de Mark Amery é mais curta e clara do que essas, então qual é o sentido delas? Por que você gostaria de usá-los?
Puramente para desempenho . A resposta de Mark decodifica o texto JSON totalmente primeiro com cadeias unicode e depois repete todo o valor decodificado para converter todas as cadeias em cadeias de bytes. Isso tem alguns efeitos indesejáveis:
Esta resposta atenua esses dois problemas de desempenho usando o
object_hook
parâmetro dejson.load
ejson.loads
. Dos documentos :Como os dicionários aninhados em muitos níveis em outros dicionários são passados à
object_hook
medida que são decodificados , podemos byteify qualquer sequência ou lista dentro deles naquele momento e evitar a necessidade de recursão profunda posteriormente.A resposta de Mark não é adequada para uso como
object_hook
está, porque se repete em dicionários aninhados. Evitamos essa recursão nesta resposta com oignore_dicts
parâmetro para_byteify
, que é passado a ele o tempo todo, exceto quandoobject_hook
passa um novodict
para byteify. Aignore_dicts
bandeira diz_byteify
para ignorardict
s, pois eles já foram byteified.Por fim, nossas implementações
json_load_byteified
ejson_loads_byteified
call_byteify
(withignore_dicts=True
) no resultado retornado dejson.load
oujson.loads
para lidar com o caso em que o texto JSON que está sendo decodificado não possui umdict
no nível superior.fonte
return { byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() }
porreturn dict((_byteify(key, ignore_dicts=True), _byteify(value, ignore_dicts=True)) for key, value in data.iteritems())
para que funcione.json_loads_byteified('[' * 990 + ']' * 990)
. Com o 991, ele trava. Mark ainda trabalha com 991:byteify(json.loads('[' * 991 + ']' * 991))
. Ele falha em 992. Portanto, pelo menos neste teste, o de Mark pode ir mais fundo, ao contrário do que você disse.Embora existam boas respostas aqui, acabei usando o PyYAML para analisar meus arquivos JSON, pois ele fornece as chaves e os valores como
str
cadeias de caracteres em vez deunicode
tipo. Como o JSON é um subconjunto do YAML, ele funciona bem:Notas
Algumas coisas a serem observadas:
Eu recebo objetos de string porque todas as minhas entradas são codificadas em ASCII . Se eu usasse entradas codificadas em unicode, as recuperaria como objetos unicode - não há conversão!
Você deve (provavelmente sempre) usar a
safe_load
função do PyYAML ; se você o usar para carregar arquivos JSON, não precisará da "energia adicional" daload
função.Se você deseja um analisador YAML que tenha mais suporte para a versão 1.2 da especificação (e analise corretamente números muito baixos ), experimente o Ruamel YAML :
pip install ruamel.yaml
eimport ruamel.yaml as yaml
era tudo o que eu precisava nos meus testes.Conversão
Como afirmado, não há conversão! Se você não pode ter certeza de lidar apenas com valores ASCII (e não pode ter certeza na maioria das vezes), use melhor uma função de conversão :
Eu usei o da Mark Amery algumas vezes agora, funciona muito bem e é muito fácil de usar. Você também pode usar uma função semelhante como
object_hook
alternativa, pois isso pode aumentar o desempenho de arquivos grandes. Veja a resposta um pouco mais envolvida de Mirec Miskuf para isso.fonte
yaml.load(json.dumps([u'a', u'£', u'É']))
no shell Python e observe que você volta['a', u'\xa3', u'\xc9']
(que contémunicode
seqüências de caracteres). Se você não tiver certeza de que seus dados contêm apenas caracteres do conjunto de caracteres ASCII, use uma abordagem diferente (recomendo minha própria resposta).[u'a', u'b']
cuidado.Não há opção integrada para fazer com que as funções do módulo json retornem sequências de bytes, em vez de sequências unicode. No entanto, essa função recursiva curta e simples converterá qualquer objeto JSON decodificado do uso de cadeias unicode em cadeias de bytes codificadas em UTF-8:
Basta chamar isso na saída obtida de uma chamada
json.load
oujson.loads
.Algumas notas:
return {byteify(key): byteify(value) for key, value in input.iteritems()}
porreturn dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])
, pois as compreensões de dicionário não eram suportadas até o Python 2.7.object_hook
ouobject_pairs_hook
. Até agora, a resposta de Mirec Miskuf é a única que consegue fazer isso corretamente, embora, como conseqüência, seja significativamente mais complicada do que minha abordagem.fonte
object_hook
é realmente muito pior do que esta, porém, mas, usandoobject_pairs_hook
, você pode criar um método razoavelmente eficiente que não exija recursão ou revisão de nós que não contêm cadeias.object_pairs_hook
método é talvez muito ligeiramente mais difícil de entender do que este (você precisa entender como funciona o parâmetro e por listas e dicts requerem um tratamento diferente), e o benefício de desempenho não importa para a maioria das pessoas ... mas eu esperaria ele existe, especialmente para quem lida com um objeto JSON aninhado profundamente incomum.Você pode usar o
object_hook
parâmetro parajson.loads
passar em um conversor. Você não precisa fazer a conversão após o fato. Ojson
módulo sempre passaráobject_hook
apenas os ditados e passará recursivamente em ditados aninhados, para que você não precise recursar em ditados aninhados. Acho que não converteria cadeias unicode em números como os de Wells. Se for uma sequência unicode, foi citada como uma sequência no arquivo JSON, portanto, deve ser uma sequência (ou o arquivo está incorreto).Além disso, eu tentaria evitar fazer algo como
str(val)
em umunicode
objeto. Você deve usarvalue.encode(encoding)
uma codificação válida, dependendo do que sua lib externa espera.Então, por exemplo:
fonte
s
for um JSONObject
(uma coleção não ordenada de chave: valor emparelha-se com o caractere ':' que separa a chave e o valor, separados por vírgula e entre chaves), mas não se for, digamos, um JSONArray
. Então, se for dada uma JSONArray
como["a", "b"]
, o resultado ainda será[u'a', u'b']
. Nenhum dos outros parâmetros de tipo de gancho de personalização disponíveis atualmente tambémjson.loads()
pode fazer o trabalho.json
módulo passará recursivamentedict
s aninhados , não é necessário procurá-los nas duas funções - portanto, as duaselif
cláusulas que os procuram devem ser removidas.from Utility import *
, as funções não serão vistas por causa desse sublinhado.object_hook
é chamado para cada objeto json analisado; portanto, se você recorrer ao que é dado, estará re-byteificando as coisas que já "byteified". O desempenho aumentará geometricamente com o tamanho do objeto. Incluí aqui uma resposta que usaobject_pairs_hook
e não sofre desse problema.Isso ocorre porque o json não tem diferença entre objetos de cadeia e objetos unicode. Eles são todos strings em javascript.
Eu acho que o JSON está certo em retornar objetos unicode . Na verdade, eu não aceitaria nada menos, já que as strings javascript são de fato
unicode
objetos (ou seja, strings JSON (javascript) podem armazenar qualquer tipo de caractere unicode), por isso faz sentido criarunicode
objetos ao traduzir strings do JSON. As strings simples simplesmente não se encaixam, pois a biblioteca precisa adivinhar a codificação desejada.É melhor usar
unicode
objetos de string em qualquer lugar. Portanto, sua melhor opção é atualizar suas bibliotecas para que elas possam lidar com objetos unicode.Mas se você realmente deseja bytestrings, apenas codifique os resultados para a codificação de sua escolha:
fonte
Existe uma solução fácil.
TL; DR - use em
ast.literal_eval()
vez dejson.loads()
. Ambosast
ejson
estão na biblioteca padrão.Embora não seja uma resposta 'perfeita', é muito importante que seu plano seja ignorar completamente o Unicode. No Python 2.7
dá:
Isso fica mais complicado quando alguns objetos são realmente cadeias Unicode. A resposta completa fica peluda rapidamente.
fonte
null
,true
oufalse
valores, porque eles não são válidos em python e fará com queliteral_eval()
a falhar.\/
) dentro de uma string ou uma sequência de escape unicode (como"\u0061"
, que é outra maneira de escrever"a"
). A sintaxe literal do Python é incompatível com o JSON de várias maneiras, e eu não confiaria nesta resposta para nenhum script que não fosse jogar fora.json
para despejar os dados, apenas useprint
se estiver executando o python. Entãoast.literal_eval
funcionaA resposta de Mike Brennan é próxima, mas não há razão para percorrer toda a estrutura. Se você usar o
object_hook_pairs
parâmetro (Python 2.7+):Com ele, você recebe cada objeto JSON para poder decodificar sem necessidade de recursão:
Observe que eu nunca preciso chamar o gancho recursivamente, pois todos os objetos serão entregues ao gancho quando você usar o
object_pairs_hook
. Você precisa se preocupar com as listas, mas, como pode ver, um objeto dentro de uma lista será convertido corretamente e não precisará recursar para que isso aconteça.Edição: Um colega de trabalho apontou que Python2.6 não tem
object_hook_pairs
. Você ainda pode usar o Python2.6 fazendo uma alteração muito pequena. No gancho acima, altere:para
Em seguida, use em
object_hook
vez deobject_pairs_hook
:O uso de
object_pairs_hook
resultados em um dicionário a menos é instanciado para cada objeto no objeto JSON, que, se você estivesse analisando um grande documento, pode valer a pena.fonte
deunicodify_hook
que você exibe nesta resposta? No momento, você tem uma implementaçãodeunicodify_hook
que não itera sobre listas e desunicodifica as seqüências e listas dentro delas; portanto, a saída que você está exibindo não corresponde à saída que seu gancho realmente produzirá. Corrija isso, e essa resposta será superior à minha.object_pairs_hook
único é chamado para objetos , se o seu texto JSON tiver uma lista de cadeias no nível superior, esta solução falhará. Não há como corrigir isso sem chamar alguma função na coisa retornadajson.load
; nenhum dosjson.load
ganchos pode garantir que você será capaz de lidar com todas as cordas. Eu acho que essa é uma falha grande o suficiente para eu continuar recomendando minha solução usando os ganchos.Receio que não haja maneira de conseguir isso automaticamente na biblioteca simplejson.
O scanner e o decodificador no simplejson foram projetados para produzir texto unicode. Para fazer isso, a biblioteca usa uma função chamada
c_scanstring
(se estiver disponível, para velocidade) oupy_scanstring
se a versão C não estiver disponível. Ascanstring
função é chamada várias vezes por quase todas as rotinas que o simplejson possui para decodificar uma estrutura que pode conter texto. Você precisaria digitar oscanstring
valor em simplejson.decoder ou subclasseJSONDecoder
e fornecer praticamente toda a sua própria implementação de qualquer coisa que possa conter texto.A razão pela qual simplejson gera unicode, no entanto, é que a especificação json menciona especificamente que "Uma string é uma coleção de zero ou mais caracteres Unicode" ... o suporte ao unicode é assumido como parte do próprio formato. A
scanstring
implementação do Simplejson chega ao ponto de varrer e interpretar escapes unicode (mesmo verificação de erros para representações de conjuntos de caracteres de bytes múltiplos malformados), portanto, a única maneira pela qual ele pode retornar o valor com segurança é como unicode.Se você tem uma biblioteca
str
antiga que precisa de um , eu recomendo que você pesquise laboriosamente a estrutura de dados aninhada após a análise (que eu reconheço é o que você disse explicitamente que queria evitar ... desculpe), ou talvez envolva suas bibliotecas em algum tipo de fachada onde você pode massagear os parâmetros de entrada em um nível mais granular. A segunda abordagem pode ser mais gerenciável que a primeira se as estruturas de dados estiverem realmente aninhadas.fonte
Como Mark (Amery) observa corretamente: Usar o desserializador do PyYaml em um dump json funciona apenas se você tiver apenas ASCII. Pelo menos fora da caixa.
Dois comentários rápidos sobre a abordagem PyYaml:
NUNCA use yaml.load nos dados do campo. É um recurso (!) Do yaml para executar código arbitrário oculto na estrutura.
Você pode fazê-lo funcionar também para não ASCII através deste:
Mas o desempenho não tem comparação com a resposta de Mark Amery:
Jogando algumas amostras profundamente aninhadas nos dois métodos, recebo isso (com dt [j] = delta time de json.loads (json.dumps (m))):
Portanto, desserialização, incluindo caminhar completamente na árvore e codificar, bem dentro da ordem de magnitude da implementação baseada em C do json. Acho isso notavelmente rápido e também mais robusto que a carga yaml em estruturas profundamente aninhadas. E menos propenso a erros de segurança, olhando para yaml.load.
=> Embora eu aprecie um ponteiro para um conversor baseado apenas em C, a função byteify deve ser a resposta padrão.
Isso vale especialmente se a sua estrutura json for do campo, contendo entrada do usuário. Porque então você provavelmente precisará percorrer de qualquer maneira sua estrutura - independentemente de suas estruturas de dados internas desejadas ('sanduíche unicode' ou apenas strings de bytes).
Por quê?
Normalização Unicode . Para quem não sabe: Pegue um analgésico e leia isso .
Então, usando a recursão byteify, você mata dois coelhos com uma cajadada:
Nos meus testes, verificou-se que substituir o input.encode ('utf-8') por um unicodedata.normalize ('NFC', input) .encode ('utf-8') era ainda mais rápido que o NFC - mas isso é fortemente dependente dos dados da amostra, eu acho.
fonte
O problema é que
simplejson
ejson
são dois módulos diferentes, pelo menos da maneira que lidam com unicode. Você temjson
em py 2.6+, e isso fornece valores unicode, enquantosimplejson
retorna objetos string. Apenas tente o easy_install-ing simplejson em seu ambiente e veja se isso funciona. Isso fez por mim.fonte
Basta usar pickle em vez de json para despejo e carregamento, da seguinte maneira:
A saída que produz é (seqüências de caracteres e números inteiros são manipulados corretamente):
fonte
safe_load
no YAML, não sei se existe algo semelhante para picles .Então, eu encontrei o mesmo problema. Adivinhe qual foi o primeiro resultado do Google.
Como eu preciso passar todos os dados para o PyGTK, as strings unicode também não são muito úteis para mim. Então, eu tenho outro método de conversão recursiva. Na verdade, também é necessário para a conversão de JSON tipesafe - json.dump () seria liberado em quaisquer literais, como objetos Python. Porém, não converte índices de dict.
fonte
Eu tinha um ditado JSON como uma string. As chaves e os valores eram objetos unicode, como no exemplo a seguir:
Eu poderia usar a
byteify
função sugerida acima convertendo a string em umdict
objeto usandoast.literal_eval(myStringDict)
.fonte
{u'key':u'value'}
não é JSON.Suporte Python2 e 3 usando gancho (em https://stackoverflow.com/a/33571117/558397 )
Devoluções:
fonte
É tarde para o jogo, mas eu construí esse lançador recursivo. Funciona para as minhas necessidades e acho que é relativamente completo. Isso pode ajudá-lo.
Apenas passe um objeto JSON assim:
Eu o tenho como um membro privado de uma classe, mas você pode redefinir o método como achar melhor.
fonte
json.loads
é necessária uma chamada primeiro), arbitrariamente tenta converter seqüências de caracteres em ints sem motivo explicado e não é copiar e copiar colar pronto.Reescrevi _parse_json () de Wells para lidar com casos em que o próprio objeto json é uma matriz (meu caso de uso).
fonte
aqui está um codificador recursivo escrito em C: https://github.com/axiros/nested_encode
Sobrecarga de desempenho para estruturas "médias" em torno de 10% em comparação com json.loads.
usando esta estrutura de teste:
fonte
Com o Python 3.6, às vezes ainda encontro esse problema. Por exemplo, ao obter resposta de uma API REST e carregar o texto de resposta no JSON, ainda recebo as seqüências unicode. Encontrei uma solução simples usando json.dumps ().
fonte
Também encontrei esse problema e, tendo que lidar com o JSON, criei um pequeno loop que converte as chaves unicode em strings. (
simplejson
no GAE não retorna chaves de sequência.)obj
é o objeto decodificado do JSON:kwargs
é o que passo ao construtor do aplicativo GAE (que não gosta deunicode
digitar**kwargs
)Não é tão robusto quanto a solução da Wells, mas muito menor.
fonte
Eu adaptei o código da resposta de Mark Amery , particularmente para me livrar de
isinstance
profissionais da digitação de patos.A codificação é feita manualmente e
ensure_ascii
está desativada. A documentação do python parajson.dump
diz queIsenção de responsabilidade: no doctest usei a língua húngara. Algumas codificações de caracteres relacionadas ao húngaro são:
cp852
a codificação IBM / OEM usada, por exemplo. no DOS (às vezes referido como ascii , incorretamente eu acho, depende da configuração da página de código ),cp1250
usado por exemplo. no Windows (às vezes chamado de ansi , dependente das configurações de localidade) eiso-8859-2
, às vezes, usado em servidores http. O texto do testeTüskéshátú kígyóbűvölő
é atribuído a Koltai László (formulário de nome pessoal nativo) e é da wikipedia .Eu também gostaria de destacar a resposta de Jarret Hardie que faz referência à especificação JSON , citando:
No meu caso de uso, eu tinha arquivos com json. Eles são
utf-8
arquivos codificados.ensure_ascii
resulta em arquivos json corretamente escapados, mas não muito legíveis, por isso adaptei a resposta de Mark Amery para atender às minhas necessidades.O doctest não é particularmente atencioso, mas eu compartilho o código na esperança de que seja útil para alguém.
fonte
json.loads
serão listas ou dictos, não algum tipo definido pelo usuário ou definido pela biblioteca que implementa seus métodos e métodos mágicos; então, por que não fazer apenas umaisinstance
verificação? Não é mais fácil entender do que verificar a existênciaiteritems
ouiter
aceitar o objeto como argumento?Confira esta resposta para uma pergunta semelhante como esta, que afirma que
O prefixo u significa apenas que você tem uma string Unicode. Quando você realmente usa a string, ela não aparece nos seus dados. Não seja jogado pela saída impressa.
Por exemplo, tente o seguinte:
Você não verá um u.
fonte
'{}'.format({u'x' : u'y'})
ainda inclui os u's.