FHEM Forum

FHEM - Entwicklung => FHEM Development => Thema gestartet von: igami am 27 September 2017, 10:21:40

Titel: Umlaute in JSON
Beitrag von: igami am 27 September 2017, 10:21:40
Ich arbeite zur Zeit an einem msgDialog modul mit dem sich ein chatBot erstellen lässt. Dabei habe ich jedoch ein Problem mit umlauten. Wenn diese in meiner Nachricht angegeben werden bekomme ich nach einiger Zeit folgende Fehlermeldung im Log:

2017.09.27 10:10:02 3: msg MichaelP: ID=1234567890.24533.1 TYPE=push ROUTE=myTelegramBot STATUS=OK PRIORITY=0 TITLE='' MSG='Ich kann folgendes für dich tun'
2017.09.27 10:10:10 3: TelegramBot_Callback myTelegramBot: resulted in NonBlockingGet timed out on read from <hidden> after 30s from SendIt
2017.09.27 10:10:10 3: TelegramBot_Callback myTelegramBot: Reached max retries (ret: NonBlockingGet timed out on read from <hidden> after 30s) for msg 50673063 : Ich kann folgendes für dich tun

Der Dialog ist als json aufgebaut:

{ "inceptum": {
    "match": "\/?(start|inceptum)",
    "message": [
      "Ich kann folgendes für dich tun:"
    ]
  },
  "abbrechen": {
    "commands":"deletereading TYPE=msgDialog %recipient%_history",
    "message": [
      "Dialog abgebrochen.",
      "",
      "/inceptum"
    ]   
  },
  "beenden": {
    "commands": "deletereading TYPE=msgDialog %recipient%_history",
    "message": [
      "Dialog beendet.",
      "",
      "/inceptum"
    ]
  }
}


Auswerten tue ich das mit

  my $dialog = encode('UTF-8', $hash->{DEF});
  $dialog = eval{decode_json($dialog)};


Wenn ich keine Umlaute angebe funktioniert es bisher wie gewollt. Kann mir jemand auf die Sprünge helfen wie ich das beheben kann?

Grüße,
igami
Titel: Antw:Umlaute in JSON
Beitrag von: rudolfkoenig am 27 September 2017, 10:26:47
Eigentlich sollte alles intern in FHEM in UTF-8 sein, auch alle gespeicherten Dateien und die Kommunikation mit der Aussenwelt (telnet/FHEMWEB) sollte diese Kodierung verwenden. Auf dem ersten Blick und ohne den Rest zu kennen sollte encode nicht notwendig sein.
Titel: Antw:Umlaute in JSON
Beitrag von: igami am 27 September 2017, 10:37:41
Wenn ich das encode weg lasse bekomme ich statt dem Umlaut im Log ein �.
Eingegeben wird alles über das DEF Feld in der Detailansicht.

Im Modul verwende ich

fhem("msg push \@$device $message");


Wenn ich eine Nachricht direkt in der Kommandozeile veranlasse kommt diese auch wie gewollt an:

msg push @MichaelP Ich kann folgendes für dich tun
oder
{fhem("msg push \@MichaelP Ich kann folgendes für dich tun")};


Momentan bin ich etwas ratlos warum es nicht funktioniert.
Titel: Antw:Umlaute in JSON
Beitrag von: viegener am 27 September 2017, 22:15:56
Ich habe endlose Stunden in die verquere Art investiert, in der perl mit unicode-Strings umgeht und kann das leider auch nicht komplett aufklären, aber folgende Informationen zu dem was passiert:

Wenn der entsprechende Fehler (resulted in NonBlockingGet timed out on read) bei telegram zurückkommt ist folgendes Problem aufgetreten:
1) Es handelt sich um einen String mit Zeichen, die nicht in einem Byte dargestellt werden können (utf-8)
2) perl markiert die Stringvariable intern als unicode-String
3) eine length operation auf dem String liefert nicht die Anzahl Bytes zurück sondern die Anzahl Zeichen (also weniger als Bytes)
4) in httputils wird beim Versenden die Content-length mit dieser inkorrekten Anzahl Bytes gesendet
5) Es kommt keine Antwort von telegram zurück

Leider unterscheiden sich auch perl-Versionen hier in der Behandlung und es gab Schwierigkeiten, die bei manchen Installationen auftraten und bei anderen nicht, ohne, dass ich die Details rausgefunden habe.

Einen Versuch wert kann es sein einen String per utf8::downgrade( zu behandeln
Titel: Antw:Umlaute in JSON
Beitrag von: igami am 27 September 2017, 22:29:29
Siehe da, mit

attr myTelegramBot utf8Special 1

funktioniert es.
Vielen Dank für die, soweit wie mögliche, Aufklärung.
Titel: Antw:Umlaute in JSON
Beitrag von: viegener am 27 September 2017, 22:45:47
Ja das wollte ich noch erwähnen, ich habe utf8Special genau für diese "unerklärlichen" Fälle hinzugefügt
Schön, dass das klappt
Titel: Antw:Umlaute in JSON
Beitrag von: igami am 29 September 2017, 11:35:31
Ich habe nun mal

  $dialog = eval{decode_json($dialog)};

durch
[/code]
  $dialog = eval{JSON->new->decode($dialog)};
[/code]
ersetzt.
"decode_json" ist eigentlich die Abkürzung für "JSON->new->utf8->decode". Ich habe also bewusst auf das UTF8 verzichtet, da es hierbei zu Probelemen kommt.

Das führt leider immer noch nicht dazu, dass ich auf das utf8Special verzichten kann, aber an der Stelle

  Debug("message: $message");
  $dialog = eval{JSON->new->encode($dialog)};
  $dialog =~ s/\$message/$message/g;
  Debug("dialog: $dialog");
  $dialog = eval{JSON->new->decode($dialog)};

hat es sonst dazu geführt, dass aus "Spülmittel" "Sp�lmittel" wurde.
Titel: Antw:Umlaute in JSON
Beitrag von: viegener am 29 September 2017, 12:24:39
Versuch doch mal folgendes (vor decode-json):

utf8::downgrade($dialog) if ( utf8::is_utf8($dialog)  ); 


Titel: Antw:Umlaute in JSON
Beitrag von: igami am 29 September 2017, 13:02:32
Zitat von: viegener am 29 September 2017, 12:24:39
Versuch doch mal folgendes (vor decode-json):

utf8::downgrade($dialog) if ( utf8::is_utf8($dialog)  ); 


Ich habe erstmal

  Debug("message is utf8") if(utf8::is_utf8($message));

  Debug("dialog is utf8") if(utf8::is_utf8($dialog));

eingebaut und stelle fest, ist kein utf8. Bleibt nur die Frage was es dann ist und wie bekomme ich heraus, was es ist?

Modul im Anhang.
Titel: Antw:Umlaute in JSON
Beitrag von: viegener am 29 September 2017, 13:13:30
Wenn aber utf8special in telegram hilft, dann wird doch irgendwo utf8 daraus (denn das schlägt auch nur zu, wenn is_utf8 gilt). Also wird irgendwo später aus message doch noch utf8

Habe gerade in telegramBot nochmal geschaut, es sieht aber nicht so aus, als ob da etwas beteiligt wäre, was unabsichtlich is_utf8 setzen könnte.

Kann das flag nachher noch hinzukommen?

Hast Du ein use utf8 in Deinem Modul?



Titel: Antw:Umlaute in JSON
Beitrag von: igami am 29 September 2017, 13:17:34
Zitat von: viegener am 29 September 2017, 13:13:30
Wenn aber utf8special in telegram hilft, dann wird doch irgendwo utf8 daraus (denn das schlägt auch nur zu, wenn is_utf8 gilt). Also wird irgendwo später aus message doch noch utf8

Habe gerade in telegramBot nochmal geschaut, es sieht aber nicht so aus, als ob da etwas beteiligt wäre, was unabsichtlich is_utf8 setzen könnte.

Kann das flag nachher noch hinzukommen?
Die Nachricht wird über den msg Befehl abgesetzt und in dem Modul gibt es die Zeile 272

            eval '$o = decode_json( Encode::encode_utf8($1) ); 1';

In meinem Modul mache ich kein utf8 draus.

Zitat von: viegener am 29 September 2017, 13:13:30
Hast Du ein use utf8 in Deinem Modul?
Ja, ist drin.
Titel: Antw:Umlaute in JSON
Beitrag von: herrmannj am 29 September 2017, 13:17:44
leider not so easy ...

utf8 kann ein bytestream (1-x mal 8 bit pro Zeichen) sein oder ein charstream (1 Zeichen darf > 8 bit sein).

Perl unterscheidet das nicht (so einfach).
is_utf8 gibt nur ein internes Flag zurück - unabhängig vom tatsächlichen Inhalt der Struktur.

Das, und verschiedene JSON und perl Versionen, ergibt zusammen den murks.

@viegener: dem Sch.. hab ich ebenfalls _einige_ dunkle Stunden ;) zu verdanken.
Titel: Antw:Umlaute in JSON
Beitrag von: igami am 29 September 2017, 13:27:30
Zu dem Zeitpunkt wo ich Prüfe ist es ja noch gar kein JSON, sonder nur der Inhalt von DEF, bzw. der Teil eines Events:

sub msgDialog_Notify($$) {
  my ($hash, $dev_hash) = @_;
  my $SELF = $hash->{NAME};
  my $TYPE = $hash->{TYPE};
  my $device = $dev_hash->{NAME};

  return if(IsDisabled($SELF));

  my @events = @{deviceEvents($dev_hash, 1)};

  return unless(
       @events
    && AttrVal($SELF, "allowed", "") =~ m/(^|,)($device|everyone)(,|$)/
  );

  foreach my $event (@events){
    next unless($event =~ m/fhemMsgPushReceived: (.+)/);

    msgDialog_progress($hash, $device, $1);
  }

  return;
}


sub msgDialog_progress($$$;$) {
sub msgDialog_progress($$$;$) {
  my ($hash, $recipients, $message, $force) = @_;
  Debug("message is utf8") if(utf8::is_utf8($message));
  my $SELF = $hash->{NAME};
  my $TYPE = $hash->{TYPE};
  $recipients =
    $recipients eq "everyone" ?
      join(",", devspec2array(
          "TYPE=(ROOMMATE|GUEST):FILTER="
        . "msgContactPush=.+:FILTER=msgRecipientPush=.+"
      ))
    : $recipients;
  my @oldHistory;
  @oldHistory = split("\\|", ReadingsVal($SELF, $recipients."_history", ""))
    unless($force);
  push(@oldHistory, split("\\|", $message));
  my (@history);
  my $dialog =$hash->{DEF};
  Debug("dialog is utf8") if(utf8::is_utf8($dialog));
  ...


"utf8::valid" ist übrigens der Meinung, dass es utf8 ist  ???
Titel: Antw:Umlaute in JSON
Beitrag von: viegener am 29 September 2017, 14:02:37
Zitat von: herrmannj am 29 September 2017, 13:17:44
leider not so easy ...

utf8 kann ein bytestream (1-x mal 8 bit pro Zeichen) sein oder ein charstream (1 Zeichen darf > 8 bit sein).

Perl unterscheidet das nicht (so einfach).
is_utf8 gibt nur ein internes Flag zurück - unabhängig vom tatsächlichen Inhalt der Struktur.

Das, und verschiedene JSON und perl Versionen, ergibt zusammen den murks.

@viegener: dem Sch.. hab ich ebenfalls _einige_ dunkle Stunden ;) zu verdanken.

Genau diese Unterscheidung meine ich mit "markiert den String als unicode" -> downgrade setzt wohl intern im wesentlichen dieses Flag zurück (meine Vermutung - trial and error) - warum das auf manchen Installationen nötig ist und bei anderen nicht verstehe ich leider immer noch nicht genau, teile aber genau Deine Vermutung

Und absolut: Murks und überhaupt nicht easy :(
Titel: Antw:Umlaute in JSON
Beitrag von: Loredo am 02 Oktober 2017, 10:14:40
Zitat von: igami am 29 September 2017, 13:17:34
Die Nachricht wird über den msg Befehl abgesetzt und in dem Modul gibt es die Zeile 272

            eval '$o = decode_json( Encode::encode_utf8($1) ); 1';

In meinem Modul mache ich kein utf8 draus.
Ja, ist drin.


Dazu kann ich 2 Dinge ergänzen:


1. Ich hasse den Sch*** genauso  8)
2. Die zitierte Codezeile kommt eigentlich nicht mehr zum Einsatz, wenn man die neue Syntax des msg Befehls verwendet, die auf parseParams aufbaut. Das heißt momentan wird sich in dem msg Befehl gar nicht um Codierung gekümmert - wie es rein kommt so geht es an den set-Befehl des jeweiligen Moduls wieder raus (theoretisch).