Sonderzeichen encoding bei JSON encode/decode?

Begonnen von mumpitzstuff, 09 November 2017, 17:41:15

Vorheriges Thema - Nächstes Thema

mumpitzstuff

Vielleicht ist ja einer der anderen Modulentwickler oder Perl Experten schon mal über dieses Problem gestolpert und kann mir kurz helfen.

Innerhalb eines eigenen Perl Moduls rufe ich ein Kommandozeilentool auf und hole mir den Output in eine Perl Variable. Irgendwann rufe ich dann encode_json() auf und wandle diese Daten in einen JSON String um. Später decodiere ich diesen String wieder mit decode_json() und hier scheint es unter bestimmten Umständen zu knallen. Meine Vermutung ist, dass das an irgendwelchen Sonderzeichen oder einem blöden encoding der Daten liegt und decode deshalb bei bestimmten Zeichen Probleme macht.

Hat sowas schon mal jemand beobachtet bzw. kann mir sagen, was man tun könnte, um den Fehler beim decode_json() Aufruf zu verhindern?

CoolTux

Was genau knallt denn? FHEM beendet sich komplett?
Rufe die JSON Funktionen in einem eval auf.
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

justme1968

unabhängig von deiner konkreten problem ursache:

da decode_json bei allen möglichen problemen aussteigt statt einen fehler zurück zu melden sollte man es immer in ein eval steckenmy $decoded = eval { decode_json($json) };
if($@) {           
  Log3 $name, 2, "$name: decode_json failed: ".$@;
}


gruß
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

mumpitzstuff

Ein eval ist schon drum, habe nur die Fehlerauswertung vergessen gehabt, deshalb knackt das Modul an der Stelle relativ unspezifisch ab. Nachdem ich aber an anderer Stelle schon Probleme hatte Umlaute mit einem regex zu finden und hier ein explizites encode_utf8 verwenden musste, vermute ich hier ein ähnliches Problem.

justme1968

regex und umlaute vertragen sich leider nicht wirklich. jedenfalls dann wenn es auf mehr als einer perl version laufen soll. zumal fhem zwar utf8 verwendet aber explizit keine perl utf8 strings.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

viegener

Ja die beliebte perl und unicode - Problematik. Hat mich im telegrambot-Modul einiges an Aufwand gekostet, da sich hier auch die perl-Versionen unterschiedlich verhalten. Wenn Du Deinen Code mal postest, kann ich versuchen zu helfen.

Kein Support über PM - Anfragen gerne im Forum - Damit auch andere profitieren und helfen können

mumpitzstuff

https://github.com/mumpitzstuff/fhem-GCALVIEW/blob/master/FHEM/57_GCALVIEW.pm

In der Funktion GCALVIEW_doEnd(). Hab auch grad gesehen das doch kein eval drum war. Aber ist auch egal ob es abkackt oder nichts raus kommt. Für den Anwender das Gleiche.

justme1968

Zitat von: mumpitzstuff am 09 November 2017, 19:58:28
Aber ist auch egal ob es abkackt oder nichts raus kommt. Für den Anwender das Gleiche.

nein :). ein modul das abschmiert ist ganz sicher schlimmer als ein modul das ab und zu bei fehleingaben nichts tut aber zumindest einen fehler ins log schreibt. ersteres sollte niemals passieren, letzteres ist zwar blöd aber so lange es nur ab und zu passiert zumindest akzeptabel. vor allem wenn den fehler trotzdem sucht und versucht zu beheben.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

mumpitzstuff

Da gebe ich dir eigentlich recht, nur in diesem Fall ist das Problem nicht temporär. Hier wird ein Kalender eingelesen. Failed das 1x dann failed das immer. Ich habs jetzt nachvollziehen können indem ich diverse Sonderzeichen in einem Kalendereintrag geschrieben habe z.B. (){}[]|<>'"´ oder ähnliches:

unexpected end of string while parsing JSON string, at character offset 321 (before "(end of string)") at ./FHEM/57_GCALVIEW.pm line 510

Ich vermute es handelt sich hier um Zeichen, die im JSON Format bereits irgend eine Bedeutung haben. Diese müsste man wahrscheinlich irgendwie escapen und hinterher wieder zurück konvertieren.

viegener

Bevor ich das Modul jetzt bei mir einrichten muss, gib vielleicht noch ein paar Informationen:

- Verstehe ich das richtig, dass das JSON von Dir erzeugt wird um die Daten zwischen dem blockingcall und der finishfn zu übertragen?
- Welche Fehlermeldung kommt denn - ich vermute, dass es auch an "nonref"-Fehlern liegen könnte?
- Bau doch mal ein eval ein wie schon vorgeschlagen da kann dann so etwas stehen:

eval {
         my $json = JSON->new->allow_nonref;
         $jo = $json->decode($calDataJson);
}

Kein Support über PM - Anfragen gerne im Forum - Damit auch andere profitieren und helfen können

viegener

Zitat von: mumpitzstuff am 09 November 2017, 22:32:18
Ich vermute es handelt sich hier um Zeichen, die im JSON Format bereits irgend eine Bedeutung haben. Diese müsste man wahrscheinlich irgendwie escapen und hinterher wieder zurück konvertieren.

Mmmh Du nicht zuerst ein base64 encoding in Deinem Code?
Kein Support über PM - Anfragen gerne im Forum - Damit auch andere profitieren und helfen können

mumpitzstuff

#11
Also ich packe die Output Daten vom Kommandozeilen Programm in ein Array rein und will jetzt eigentlich nur dieses Array vom BlockingCall in die finishfn übertragen und dort wieder dieses array verwenden.
encode/decode json hat den charmanten Vorteil, das man hier quasi ein array einpacken und auch wieder auspacken kann. Allerdings macht das jetzt anscheinend erhebliche Probleme bei im JSON Format vorbelegten Zeichen wie z.B. ". Die jetzt alle zu escapen und unescapen wird eine elende Fummelei.

Deshalb andere Frage, die dieses Problem lösen würde:

Wie kann man ein Array in einen String umwandeln und aus diesem String wieder ein Array generieren, ohne dabei Probleme mit diversen Sonderzeichen zu erhalten?

mumpitzstuff

Ich glaub ich habs:


use Storable qw(freeze thaw);

$calData = eval {encode_base64(freeze(\@calStruct), '')};

@calData = eval {@{thaw(decode_base64($calDataJson))} if ('' ne $calDataJson)};


Damit kackt mir das Modul zumindest nicht mehr ab.

herrmannj

#13
json ist der richtige Weg.

* json (richtig verwendet) überträgt Text(!) Daten, kümmert sich selber um die richtige codierung, escaped selbstständig alle Sonderzeichen.
* die perl json module sind doof weil sie bei jeder sich bietenden Gelegenheit Fehler werfen die zum Abbruch des Programmes führen.
* Mime64 ist für Binärdaten (0x00 .. 0xff). Bei Unicode Zeichenketten nicht mehr verwendbar weil Unicode Zeichen enthält (enthalten kann) welche > 0xff sind.

JSON decode erwartet einen utf8 byte(!) Stream. Das Problem hier ist das perl bei diversen IO Operationen eigene Konvertierungen vornimmt und es oft unklar ist ob das nun ein UNICODE CHARACTER ist (Besteht aus einem einzigen Zeichen) oder ob es sich um einen UTF8 Bytestream handelt. Wenn man sich das ausgeben lässt sieht beides gleich aus ...

Testen kann man das jedoch. Dieses Zeichen 我 ist irgendwas chinesisches. Wenn man das (und jedes andere chinesische, kyrillische usw) in einen String kopiert, es dann überträgt und anschließend auf das was ankommt ein "length($in)" loslässt.  Kommt dabei eine 1 raus handelt es sich um einen String mit UNICODE CHARACTER. Der darf nicht in JSON decode rein. Kommt eine Zahl > 1 raus ist es ein bytestream. Der darf rein. (EDIT: Wenn das passiert muss man sich die Übertragungskette anschauen und reparieren. Dann wird bei der Übertragung unzulässig de- und kodiert)

Wichtig ist es das wirklich sauber (beim Entwickeln) zu testen. Sonst kann es durchaus sein das alles gut zu sein scheint. Überträgt man nur "normale" Zeichen klappt alles. Wenn dann doch, durch Zufall, plötzlich ein Sonderzeichen (so etwas á) in der Übertragung auftaucht macht es buff ...

vg
joerg

edit in Absatz 3 zur Klarstellung ergänzt, de- kodierung

mumpitzstuff

Ich hatte versucht den output der Konsolenapplikation als Ganzes mit encode_utf8 umzuwandeln, dann zu verarbeiten und dann in json zu konvertieren. Da ist mir das decode_json auch wieder abgeschmiert. Wahrscheinlich weil bei der Verarbeitung Perl wieder irgendwas umgewandelt hat (hast du ja auch erwähnt). Eventuell könnte hier ein encode_utf8 direkt beim pushen ins Array helfen, das man dann später zu json konvertiert. Wenn ich mehr Zeit habe probiere ich das der Vollständigkeit halber auch mal aus.

Danke für die ausführlichen Informationen!