J'ai des données JSON stockées dans une colonne JSON (pas JSONB) de ma base de données postgresql (9.4.1). Certaines de ces structures JSON contiennent des séquences unicode dans leurs valeurs d'attribut. Par exemple:
{"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" }
Lorsque j'essaie d'interroger cette colonne JSON (même si je n'essaie pas directement d'accéder à l'attribut device_name
), j'obtiens le message d'erreur suivant:
ERREUR: séquence d'échappement Unicode non prise en charge
Détail:\u0000
ne peut pas être converti en texte.
Vous pouvez recréer cette erreur en exécutant la commande suivante sur un serveur postgresql:
select '{"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" }'::json->>'client_id'
L'erreur a du sens pour moi: il n'y a tout simplement aucun moyen de représenter la séquence unicode NULL
dans un résultat textuel.
Puis-je interroger les mêmes données JSON sans avoir à effectuer un "assainissement" des données entrantes? Ces structures JSON changent régulièrement, aussi l'analyse d'un attribut spécifique (device_name
dans ce cas) ne serait pas une bonne solution car il pourrait facilement y avoir d'autres attributs pouvant contenir des données similaires.
Après quelques investigations supplémentaires, il semble que ce comportement soit nouveau pour la version 9.4.1 en tant que mentionné dans le journal des modifications :
... Par conséquent,
\u0000
sera désormais également rejeté dans les valeurs json lorsque la conversion en formulaire échappé est requise. Cette modification ne rompt pas la possibilité de stocker\u0000
dans les colonnes JSON tant qu'aucun traitement n'est effectué sur les valeurs ...
Était-ce vraiment l'intention? Une mise à niveau antérieure à 9.4.1 est-elle une option viable ici?
En note, cette propriété est tirée du nom du périphérique mobile du client - c’est l’utilisateur qui a saisi ce texte dans le périphérique. Comment diable un utilisateur at-il inséré NULL
et REPLACEMENT CHARACTER
values?!
\u0000
est le seul point de code Unicode qui n'est pas valide dans une chaîne. Je ne vois pas d'autre moyen que de nettoyer la chaîne.
Comme json
est juste une chaîne dans un format spécifique, vous pouvez utiliser les fonctions de chaîne standard sans vous soucier de la structure JSON. Un désinfectant à une ligne pour supprimer le point de code serait:
SELECT (regexp_replace(the_string::text, '\\u0000', '', 'g'))::json;
Mais vous pouvez également insérer n'importe quel caractère de votre choix, ce qui serait utile si le point de code zéro est utilisé comme une forme de délimiteur.
Notez également la différence subtile entre ce qui est stocké dans la base de données et sa présentation à l'utilisateur. Vous pouvez stocker le point de code dans une chaîne JSON, mais vous devez le pré-traiter sur un autre caractère avant de traiter la valeur en tant que type de données json
.
La solution de Patrick n'a pas fonctionné pour moi. Peu importe, il y avait toujours une erreur. J'ai ensuite fait un peu plus de recherches et j'ai pu écrire une petite fonction personnalisée qui corrigeait le problème.
Tout d'abord, je pourrais reproduire l'erreur en écrivant:
select json '{ "a": "null \u0000 escape" }' ->> 'a' as fails
Ensuite, j'ai ajouté une fonction personnalisée que j'ai utilisée dans ma requête:
CREATE OR REPLACE FUNCTION null_if_invalid_string(json_input JSON, record_id UUID)
RETURNS JSON AS $$
DECLARE json_value JSON DEFAULT NULL;
BEGIN
BEGIN
json_value := json_input ->> 'location';
EXCEPTION WHEN OTHERS
THEN
RAISE NOTICE 'Invalid json value: "%". Returning NULL.', record_id;
RETURN NULL;
END;
RETURN json_input;
END;
$$ LANGUAGE plpgsql;
Pour appeler la fonction, faites ceci. Vous ne devriez pas recevoir d'erreur.
select null_if_invalid_string('{ "a": "null \u0000 escape" }', id) from my_table
Considérant que cela devrait retourner le JSON comme prévu:
select null_if_invalid_string('{ "a": "null" }', id) from my_table