PHP Array mit chinesischen Schriftzeichen als JSON an Python übergeben

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
pythonmython
User
Beiträge: 3
Registriert: Donnerstag 27. Juli 2017, 21:06

Hallo zusammen,

ich möchte ein PHP Array als JSON an ein Python-Script übergeben. Im Python Script möchte ich es dann als Liste weiter verarbeiten. Die Besonderheit ist, dass das JSON einige Sonderzeichen sowie chinesische Schriftzeichen enthält.

Nutze ich in PHP lediglich JSON encode ohne Zusatzoption und übergebe das generierte JSON an Python, sagt Python es handele sich dabei um kein valides JSON:

PHP:

Code: Alles auswählen

$unknown = $_GET['unknown'];
$unknown = json_encode($unknown);
exec("/usr/bin/python script.py 2>&1 $unknown", $out, $result);
Python-Fehlermeldung

[9] => raise ValueError("No JSON object could be decoded")
[10] => ValueError: No JSON object could be decoded

---------

Um zu erzwingen, dass ein korrektes JSON aus PHP erzeugt wird, habe ich folgendes versucht:

PHP

Code: Alles auswählen

$unknown = json_encode($unknown, JSON_FORCE_OBJECT);
exec("/usr/bin/python exporter.py 2>&1 $unknown", $out, $result);
Laut JSON-Linter handelt es sich nun um ein valides JSON, welches auch von Python als solches erkannt wird.

JSON

[codebox=javascript file=Unbenannt.js]{
"0": "\u5168\u7403 [quan2 qiu2] \/entire\/total\/global\/the (whole) world\/worldwide\/\r\n",
"1": "|| \u89c6\u89c9 [shi4 jue2] \/sight\/vision\/visual\/\r\n",
"2": "|| \u9876\u7ea7 [ding3 ji2] \/top-notch\/first-rate\/\r\n",
"3": "|| \u4e0b\u4e00\u4e2a [xia4 yi1 ge5] \/the next one\/\r\n"
}[/code]

In Python versuche ich, dass JSON folgendermaßen auszulesen:

Code: Alles auswählen

import sys
import json

data = json.loads(sys.argv[1])

print data
Hier erhalte ich nun aber folgenden Fehler:

Code: Alles auswählen

Array
(
    [0] => 0:|| \u4f5c\u8005 [zuo4 zhe3] \/author\/writer\/CL:\u500b|\u4e2a[ge4]\/\r\n
    [1] => Traceback (most recent call last):
    [2] =>   File "script.py", line 10, in 
    [3] =>     data = json.loads(sys.argv[1])
    [4] =>   File "/usr/lib/python2.7/json/__init__.py", line 338, in loads
    [5] =>     return _default_decoder.decode(s)
    [6] =>   File "/usr/lib/python2.7/json/decoder.py", line 369, in decode
    [7] =>     raise ValueError(errmsg("Extra data", s, end, len(s)))
    [8] => ValueError: Extra data: line 1 column 2 - line 1 column 76 (char 1 - 75)
)
Hat jemand vielleicht eine Idee, wie ich das JSON korrekt an Python übergeben kann?
Zuletzt geändert von Anonymous am Sonntag 6. August 2017, 09:33, insgesamt 1-mal geändert.
Grund: Quelltext in Codebox-Tags gesetzt.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mit sieht das so aus als ob da die zwischengeschaltete Shell ein Problem darstellt. Versuch doch mal testweise dein JSON von PHP in eine Datei schreiben zulassen, und die dann einzulesen in Python.
BlackJack

@pythonmython: `JSON_FORCE_OBJECT` ist nicht notwendig, Python kann auch JSON-Arrays dekodieren:

Code: Alles auswählen

In [1]: import json

In [2]: json.loads('[1, 2, 3]')
Out[2]: [1, 2, 3]
Das Problem ist `exec()` in PHP und das Du das JSON da einfach als Kommandozeilenargument verwendest. Bevor Python das zu sehen bekommt, muss das erst einmal an der Shell vorbei und für die haben Zeichen aus dem Inhalt auch eine Bedeutung und die interpretiert diese Zeichen.

Aus dem JSON-Array macht die Shell dann das hier:
[codebox=text file=Unbenannt.txt][\u5168\u7403 [quan2 qiu2] \/entire\/total\/global\/the (whole) world\/worldwide\/\r\n,|| \u89c6\u89c9 [shi4 jue2] \/sight\/vision\/visual\/\r\n,|| \u9876\u7ea7 [ding3 ji2] \/top-notch\/first-rate\/\r\n,|| \u4e0b\u4e00\u4e2a [xia4 yi1 ge5] \/the next one\/\r\n][/code]

Und aus dem JSON-Objekt dieses:
[codebox=text file=Unbenannt.txt]0:\u5168\u7403 [quan2 qiu2] \/entire\/total\/global\/the (whole) world\/worldwide\/\r\n 1:|| \u89c6\u89c9 [shi4 jue2] \/sight\/vision\/visual\/\r\n 2:|| \u9876\u7ea7 [ding3 ji2] \/top-notch\/first-rate\/\r\n 3:|| \u4e0b\u4e00\u4e2a [xia4 yi1 ge5] \/the next one\/\r\n[/code]

Beides ist kein gültiges JSON mehr und darum kann Python's `json`-Modul damit nichts anfangen.

Dass das so nicht funktioniert ist übrigens noch nicht das schlimmste! Du verwendest Benutzereingaben einfach so als Argument um ein Programm auszuführen — ein nicht so netter Benutzer könnte das verwenden um beliebige Programmaufrufe auf Deinem Webserver auszuführen! Vertraue niemals Benutzereingaben!

Man könnte nun versuchen dafür zu sorgen das die Zeichen die für die Shell eine besondere Bedeutung haben vor der Shell geschützt werden, allerdings funktioniert das, zumindest mit PHP-Bordmitteln nicht plattformübergreifend. Denn unter Windows ersetzt `escapeshellarg()` einige Zeichen einfach durch Leerzeichen und macht damit das JSON auch kaputt. Wenn das also auch auf Windows laufen können soll, wäre `exec()` die falsche Funktion und man müsste sich etwas mit `proc_open()` & Co schreiben und die Daten über die Standardein/-augabe übertragen.
pythonmython
User
Beiträge: 3
Registriert: Donnerstag 27. Juli 2017, 21:06

Hallo zusammen,

vielen Dank für das Feedback. Ich habe es nun tatsächlich lösen können, indem ich das Array nicht via exec übergebe, sondern in PHP ein separates JSON File schreiben lasse. Dieses JSON File lässt sich dann tatsächlich direkt in Python importieren.

Ich wundere mich zwar, dass es hier keinen direkten Weg zu geben scheint, da das übergeben von Arrays an Python ja ein Standard-Usecase sein sollte? Aber es funktioniert jetzt erstmal so. Natürlich werden die Eingaben noch entsprechend geschützt, diente nur zu Testzwecken.
BlackJack

@pythonmython: Auch beim Testfall sollte man die Daten bei der Übergabe nicht kaputt machen beziehungsweise dafür sorgen das sie durch zwischengeschaltete Prozesse wie eine Shell nicht kaputt gemacht werden. Das ist doch genau das Problem das Du hier hast.

Ich sehe nicht wie oder warum das ein Standard-Usecase sein sollte und was das mit Python zu tun hat. Wenn Du einen externen Prozess startest und dem Daten übergeben willst, dann hast Du unabhängig von der Sprache in der das externe Programm geschrieben ist immer die gleichen Möglichkeiten. Ausser die Sprache unterstützt einen der üblichen Wege nicht. Falls Du das Programm das Du mit `exec()` startest in PHP geschrieben hättest, dann hättest Du vor exakt dem gleichen Problem gestanden. Auch da hätte Dir die Shell ohne entsprechendes „escapen“ die JSON-Daten kaputt gemacht.

Du kannst Daten beim Aufruf in der Kommandozeile übergeben — da ist die Grösse begrenzt und man muss bei zwischengeschalteter Shell dafür sorgen das Zeichen die für die Shell eine besondere Bedeutung haben, entsprechend geschützt sind. Du kannst die Daten über die Standardeingabe in den externen Prozess schreiben. Oder der externe Prozess macht ein Serversocket auf und Du überträgst die Daten über das Socket. Oder Du verwendest eine temporäre Datei. Das sind so im groben die gängigen Möglichkeiten.
pythonmython
User
Beiträge: 3
Registriert: Donnerstag 27. Juli 2017, 21:06

Danke für die ausführliche Erklärung, werde es beachten :)
Antworten