Umlaute von Website gehen verloren

  • Antworten:18
  • Bentwortet
Fragensteller
  • Forum-Beiträge: 49

08.09.2018, 17:29:30 via Website

Ich hole mir json daten von einer website mittels httphandler:

public class HttpHandler {

private static final String TAG = HttpHandler.class.getSimpleName();

public HttpHandler() {
}

public String makeServiceCall(String reqUrl) {
    String response = null;
    try {
        URL url = new URL(reqUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        // read the response
        InputStream in = new BufferedInputStream(conn.getInputStream());
        response = convertStreamToString(in);
    } catch (MalformedURLException e) {
        Log.e(TAG, "MalformedURLException: " + e.getMessage());
    } catch (ProtocolException e) {
        Log.e(TAG, "ProtocolException: " + e.getMessage());
    } catch (IOException e) {
        Log.e(TAG, "IOException: " + e.getMessage());
    } catch (Exception e) {
        Log.e(TAG, "Exception: " + e.getMessage());
    }
    return response;
}

private String convertStreamToString(InputStream is) {
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    StringBuilder sb = new StringBuilder();

    String line;
    try {
        while ((line = reader.readLine()) != null) {
            sb.append(line).append('\n');
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return sb.toString();
}

}

Leider sind im String hinterher alle Umlaute verloren gegangen.

HttpHandler sh = new HttpHandler();
jsonStr = sh.makeServiceCall(url);

Ich müsste sicher irgendwo beim download das charset einstellen, weiß aber nicht wie.

Kommentieren
Beste Antwort
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

09.09.2018, 15:25:40 via Website

Hast du überprüft ob due die synchrone oder anynchrone Variante von TTS benutzt?
Die synchrone wirst du nicht unterbrechen können, die asynchrone schon.

Zum Json Parsing:

Das ist an sich garnicht so komplex, deine Variante hat aber aktuell den nachteil, denn was passiert wenn deine Page oder normalzed plötzlich mehrere Elemente zurückliefert? Erfasst du dann trotzdem alle?

Mit der Json Parse Variante hast du alle, zumal dann noch evtl in einem passenden Objekt:

Hier mal die passenden Zeilen für deine Json mit egal wievielen Einträgen (Exceptionhandling nicht berücksichtigt):

Haupt Pase Methode mit input String:

 try {
            JSONObject jsonObj = new JSONObject(json);

            String batchComplete = jsonObj.getString("batchcomplete");

           Query query = new Query(jsonObj.getJSONObject("query"));

           String t = query.normalized.get(0).from; // beispielzugriff auf normalizes[0].from geht mit gettern und settern sicherlich besser

        } catch (JSONException e) {
            e.printStackTrace();
        }

Aufbau des Query Objekts:

public class Query {

    List<Relation> normalized  = new ArrayList<Relation>();
    List<Relation> redirect  = new ArrayList<Relation>();

    Map<String,PageData> pages = new HashMap<String,PageData>();

    public Query(JSONObject query) throws JSONException {

        //alle normalizes auslesen und in die Liste packen
        JSONArray normalizedArray  = query.getJSONArray("normalized");
        for (int i = 0; i < normalizedArray.length(); i++) {
            normalized.add(new Relation(normalizedArray.getJSONObject(i)));
        }

        //alle redriects lesen
        JSONArray redirectsArray  = query.getJSONArray("redirects");
        for (int i = 0; i < redirectsArray.length(); i++) {
            redirect.add(new Relation(redirectsArray.getJSONObject(i)));
        }

        JSONObject pagesJson = query.getJSONObject("pages");        // Pages als Map parsen
        Iterator<String> keyIterator = pagesJson.keys();
        while(keyIterator.hasNext()){
          String key =   keyIterator.next();
          JSONObject currObj = pagesJson.getJSONObject(key);
          PageData page = new PageData(currObj);
          pages.put(key,page);
        }

    }
}

class Relation {

    String from;
    String to;
    public Relation(JSONObject data) throws JSONException {

        this.from = data.getString("from");
        this.to = data.getString("to");
    }

}

class PageData {

    int pageId;
    int ns;
    String title;
    String extract;

    public PageData(JSONObject data) throws JSONException {

        pageId = data.getInt("pageid");
        ns = data.getInt("ns");
        title = data.getString("title");
        extract = data.getString("extract");
    }

}

Damit hast du eine Allgemeine lösung Objektorientiert und in 60 Zeilen.
Jetzt programmier mir das ding mal mit Splits sodass du mit weniger Zeilen alle Einträge erfasst ;)
Überleg dir was du nutzen willst... :O

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Kommentieren
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

08.09.2018, 17:38:06 via App

Hallo Fragensteller,

das kannst du im InputStreamReader festlegen:
https://stackoverflow.com/a/4964652

Da du den WebCall allerdings aus dem MainThread machst, wirst du früher oder später Probleme bekommen.
Nutze entweder eine. AsyncTask oder gleich eine gescheite HTTP Lib wie Ion oder okhttp

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
swa00
Kommentieren
Fragensteller
  • Forum-Beiträge: 49

08.09.2018, 18:15:13 via Website

Hi Pascal,

danke für die schnelle Antwort. Der WebCall erfolgt bereits mit einer AsyncTask, hab jetzt nur den ganzen Code nicht aufgeführt.
Ich habe jetzt alle möglichen Eingaben getestet:

private String convertStreamToString(InputStream is) {
    BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
    StringBuilder sb = new StringBuilder();

private String convertStreamToString(InputStream is) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.ISO_8859_1));
        StringBuilder sb = new StringBuilder();

usw...

Die Umlaute kommen einfach nicht mit.
\u00e4 für ä
\u00f6 für ü
usw.

Hilfreich?
Kommentieren
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

08.09.2018, 18:27:03 via App

Du weißt wie Json funktioniert oder?
Da du einen Json string überträgst, sind die Umlaute im Json schon als unicode dargestellt. (damit gerade ein von dir vermuteter Übertragungsfehler nicht passieren kann)
Du musst den String durch einen Json Parser schicken, dann passt alles.

Wenn die Json Struktur gleichbleibend ist, schau die Google Gson an, der kann dir das gleich in ein Java Objekt parsen.
Ansonsten mit dem Java Standard Json Parser.

— geändert am 08.09.2018, 18:27:51

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Kommentieren
Fragensteller
  • Forum-Beiträge: 49

08.09.2018, 18:51:05 via Website

Ich wollte den Parser vermeiden, weil das immer so aufwendig ist die richtigen Arrays, Objekte und co zu finden. Da mir die Datei schon als String vorliegt, wollt ich die gleich weiter verarbeiten.
Hab schon versucht die codes mit replace zu ersetzen. aber

if (ergebnis.contains("\u00e4")) {ergebnis=wikiergebnis.replace("\u00e4","ä");}

Das \ wird aber nicht als Textteil sondern als Befehl verstanden.

Hilfreich?
Kommentieren
Fragensteller
  • Forum-Beiträge: 49

08.09.2018, 19:07:57 via Website

Jetzt gehts:

if (ergebnis.contains("\\u00e4")) {ergebnis=wikiergebnis.replace("\\u00e4","ä");}

Ist jetzt zwar nicht die Lösung des ursprünglichen Problems, aber auch eine Lösung.
Das Ergebnis zählt ja am Ende ;)

Hilfreich?
Kommentieren
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

08.09.2018, 19:15:20 via App

Willst du die Json mal posten? Mit dem passenden Java Objekt ind Gson ist das Parsen in 2 Codezeilen erledigt ;)
Nix mit Aufwändigen Array rausziehen etc.

So hast du später ein Problem wenn sich dein Inhalt ändert...
Außerdem: Wenn kein Parser wie wertest du die Json aus? Doch nicht etwa mit Substring und Split etc. das ist noch kompexer...

— geändert am 08.09.2018, 19:16:54

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
swa00
Kommentieren
Fragensteller
  • Forum-Beiträge: 49

09.09.2018, 09:52:20 via Website

Das ist eine Json, die ich erhalte:

{"batchcomplete":"","query":{"normalized":[{"from":"hund","to":"Hund"}],"redirects":[{"from":"Hund","to":"Haushund"}],"pages":{"2527125":{"pageid":2527125,"ns":0,"title":"Haushund","extract":"Der Haushund (Canis lupus familiaris) ist ein Haustier und wird als Heim- und Nutztier gehalten. Seine wilde Stammform ist der Wolf, dem er als Unterart zugeordnet wird. Wann die Domestizierung stattfand, ist umstritten; wissenschaftliche Sch\u00e4tzungen variieren zwischen 15.000 und 100.000 Jahren vor unserer Zeit.\nIm engeren Sinn bezeichnet man als Haushund die Hunde, die \u00fcberwiegend im Haus gehalten werden, und  kennzeichnet damit also eine Haltungsform. Historisch wurde ein Hund, der zur Bewachung des Hauses gehalten wird, als Haushund bezeichnet. Eine weitere Verwendung des Begriffs ist die Einschr\u00e4nkung auf sozialisierte (Haus-)Hunde, also Hunde, die an das Zusammenleben mit Menschen in der menschlichen Gesellschaft gew\u00f6hnt und an dieses angepasst sind. Damit wird der Haushund abgegrenzt gegen wild lebende, verwilderte oder streunende Hunde, die zwar auch domestiziert, aber nicht sozialisiert sind.Der Dingo ist ebenfalls ein Haushund, wird jedoch provisorisch als eigenst\u00e4ndige Unterart des Wolfes gef\u00fchrt."}}}}

Das "pages" Object hat immer eine andere id, diese kenne ich vorher nicht. "redirects" Array ist auch nicht immer da.
Mit Split sind das nur 2 Zeilen code, einfach alles vor extract rausschmeißen. Gut, die Umlaute wiederherstellen sind nochmal 4 Zeilen code.

— geändert am 09.09.2018, 10:01:25

Hilfreich?
Kommentieren
Gelöschter Account
  • Forum-Beiträge: 21.034

09.09.2018, 10:02:03 via Website

Du bekommst eine UTF-8-kodierte Datei, behandelst sie aber ISO8859-1(5)-kodiert.

Hilfreich?
Kommentieren
swa00
  • Forum-Beiträge: 3.704

09.09.2018, 10:23:46 via Website

Hallo,

ich weis ja nicht , warum du alles zu Fuss laden & parsen willst und damit dir eigentlich nur zusäzliche Probleme ins Haus holst, die nicht sein müssen und worüber sich schon Andere bereits Gedanken gemacht haben .
Im Prinzip brennst du für deine Mauer deine eigenen Ziegel , obwohl man sie liefern lassen und sofort loslegen kann. - Aber jedem das Seine.

Mach es doch einfach , wie jeder von uns es mit den "OnBoard" Mitteln tut.

GET : ION, OKHttp,Volley - incl. wunderbarer Fehlerbehandlung und Listener.
z.B. https://developer.android.com/training/volley/

Parser :https://developer.android.com/reference/android/util/JsonReader

Maximal 20-30 Zeilen und 30 Minuten Aufwand.
Alles schon da ..... und vor allem geprüft

    String url = "Deine url"";
    RequestQueue requestQueue = Volley.newRequestQueue(mActivity);
    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET,url,null,
            new Response.Listener<JSONObject>()
            {
                @Override
                public void onResponse(JSONObject response)
                {
                    try
                    {
                        JSONObject rootobject = response.getJSONObject("rootobject");
                        JSONArray  rootarray= rootobject.getJSONArray("rootarray");
                        for (int i = 0; i < rootarray.length(); i++)
                        {
                           JSONObject item = rootarray.getJSONObject(i); 
                           String myvalue = item.getString("myvalue");
                        }

— geändert am 09.09.2018, 11:39:12

Liebe Grüße - Stefan
[ App - Entwicklung ]

Hilfreich?
Pascal P.
Kommentieren
Fragensteller
  • Forum-Beiträge: 49

09.09.2018, 11:36:57 via Website

Ok, ich versuche mein Json da einzubauen:
myvalue wäre extract
rootobject ist wahrscheinlich 2527125, nur hier ist das Problem, der Wert ist immer anders.
pages ist auch ein Object, oder?
was ist jetzt mein rootarray?

Hilfreich?
Kommentieren
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

09.09.2018, 11:42:07 via Website

Du musst das dann anders machen.

pages ist dein JsonObject, und dieses musst du dann durchgehen und dir die Werte als Map rausziehen:
https://stackoverflow.com/a/48245094

damit bist du unabhängig der "Keys" im Json und hast die aber trotzdem mit zugeordneter ID

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
swa00
Kommentieren
Fragensteller
  • Forum-Beiträge: 49

09.09.2018, 14:35:11 via Website

Danke, wird ja doch irgendwie immer komplizierter :(. Das ganze werde ich mal die Tage durchgehen, bis dahin funktioniert die Split Methode ganz gut.
Ein anderes kleins Problem hät ich noch: Ich lass mir das Ergebnis mit tts vorlesen:

tts.speak(sprachausgabe, TextToSpeech.QUEUE_FLUSH, null);

Ist ganz lustig, wenn ich jetzt aber das tts abbrechen will habe ich einen Button und einen OnTouchListener:

public boolean onTouch(View v, MotionEvent event) {


    switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN:

            Log.e(TAG, "SpeakButton gedrückt");
            if (tts.isSpeaking()) {
                tts.stop();
             }

            return true;
        case MotionEvent.ACTION_MOVE:

            return true;

        case MotionEvent.ACTION_UP:
            Log.e(TAG, "SpeakButton nicht gedrückt");

            return true;
    }
    return super.onTouchEvent(event);
}

Wenn die tts spricht, kann ich den Knopf drücken soviel ich will, keine Reaktion. Wenn die tts fertig ist kann ich im Log sehen wie oft ich gedrückt habe. Der Befehl wird angenommen, aber erst ausgeführt, wenn die tts fertig ist. Das gleiche bei destroy:

  @Override
public void onDestroy() {
    if (tts != null) {
        tts.stop();
        tts.shutdown();

    }
    super.onDestroy();
}

Wenn ich das Programm beende, blabbert die tts weiter.

Hilfreich?
Kommentieren
Beste Antwort
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

09.09.2018, 15:25:40 via Website

Hast du überprüft ob due die synchrone oder anynchrone Variante von TTS benutzt?
Die synchrone wirst du nicht unterbrechen können, die asynchrone schon.

Zum Json Parsing:

Das ist an sich garnicht so komplex, deine Variante hat aber aktuell den nachteil, denn was passiert wenn deine Page oder normalzed plötzlich mehrere Elemente zurückliefert? Erfasst du dann trotzdem alle?

Mit der Json Parse Variante hast du alle, zumal dann noch evtl in einem passenden Objekt:

Hier mal die passenden Zeilen für deine Json mit egal wievielen Einträgen (Exceptionhandling nicht berücksichtigt):

Haupt Pase Methode mit input String:

 try {
            JSONObject jsonObj = new JSONObject(json);

            String batchComplete = jsonObj.getString("batchcomplete");

           Query query = new Query(jsonObj.getJSONObject("query"));

           String t = query.normalized.get(0).from; // beispielzugriff auf normalizes[0].from geht mit gettern und settern sicherlich besser

        } catch (JSONException e) {
            e.printStackTrace();
        }

Aufbau des Query Objekts:

public class Query {

    List<Relation> normalized  = new ArrayList<Relation>();
    List<Relation> redirect  = new ArrayList<Relation>();

    Map<String,PageData> pages = new HashMap<String,PageData>();

    public Query(JSONObject query) throws JSONException {

        //alle normalizes auslesen und in die Liste packen
        JSONArray normalizedArray  = query.getJSONArray("normalized");
        for (int i = 0; i < normalizedArray.length(); i++) {
            normalized.add(new Relation(normalizedArray.getJSONObject(i)));
        }

        //alle redriects lesen
        JSONArray redirectsArray  = query.getJSONArray("redirects");
        for (int i = 0; i < redirectsArray.length(); i++) {
            redirect.add(new Relation(redirectsArray.getJSONObject(i)));
        }

        JSONObject pagesJson = query.getJSONObject("pages");        // Pages als Map parsen
        Iterator<String> keyIterator = pagesJson.keys();
        while(keyIterator.hasNext()){
          String key =   keyIterator.next();
          JSONObject currObj = pagesJson.getJSONObject(key);
          PageData page = new PageData(currObj);
          pages.put(key,page);
        }

    }
}

class Relation {

    String from;
    String to;
    public Relation(JSONObject data) throws JSONException {

        this.from = data.getString("from");
        this.to = data.getString("to");
    }

}

class PageData {

    int pageId;
    int ns;
    String title;
    String extract;

    public PageData(JSONObject data) throws JSONException {

        pageId = data.getInt("pageid");
        ns = data.getInt("ns");
        title = data.getString("title");
        extract = data.getString("extract");
    }

}

Damit hast du eine Allgemeine lösung Objektorientiert und in 60 Zeilen.
Jetzt programmier mir das ding mal mit Splits sodass du mit weniger Zeilen alle Einträge erfasst ;)
Überleg dir was du nutzen willst... :O

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Kommentieren
Fragensteller
  • Forum-Beiträge: 49

09.09.2018, 16:22:12 via Website

Ich denke, das hätte ich nicht hinbekommen. Ich hab das mal eingebaut:

DownloadManager.Query query = new DownloadManager.Query(jsonObj.getJSONObject("query"));

hier nörgelt AS an jsonObj.getJSONObject("query") rum und bei

String t = query.normalized.get(0).from;

kann AS nichts mit normalized anfangen. Die Klasse Querry wird als ungenutzt angezeigt, vielleicht hab ich das falsch zusammengesetzt:

private class GetWiki extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... arg0) {
        HttpHandler sh = new HttpHandler();

        String url = "https://de.wikipedia.org/w/api.php?action=opensearch&search="+code+"&limit=1&format=json";
        jsonStr = sh.makeServiceCall(url);

        Log.e(TAG, "Response from url: " + jsonStr);
        if (jsonStr != null) {

            try {
                JSONObject jsonObj = new JSONObject(jsonStr);

                String batchComplete = jsonObj.getString("batchcomplete");

                DownloadManager.Query query = new DownloadManager.Query(jsonObj.getJSONObject("query"));

                String t = query.normalized.get(0).from; // beispielzugriff auf normalizes[0].from geht mit gettern und settern sicherlich besser

            } catch (JSONException e) {
                e.printStackTrace();
            }

        } else {
            Log.e(TAG, "Couldn't get json from server.");
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(),
                            "Kann die Daten nicht finden",
                            Toast.LENGTH_LONG).show();
                }
            });
        }

        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);

    }
}

Die Klassen hab ich hier hingesetzt.

Es gibt 2 Varianten von tts? Oder muss ich dann auch eine Klasse mit ASynchTask bauen?

— geändert am 09.09.2018, 16:40:46

Hilfreich?
Kommentieren
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

09.09.2018, 16:38:32 via App

Die Java grundlagen kann ich dir jetzt aber nicht erklären z.b. Sichtbarkeit der Klassen/Variablen via Public/private im und außerhalb vom Package...
Zudem sollte jede Klasse in eigene Dateien...

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Kommentieren
Fragensteller
  • Forum-Beiträge: 49

09.09.2018, 17:25:37 via Website

Note that speak() calls are asynchronous

Die tts lässt sich auch stoppen:

tts.speak(sprachausgabe, TextToSpeech.QUEUE_FLUSH, null);
tts.stop();

nur nicht bei destroy oder button.

Hilfreich?
Kommentieren
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

09.09.2018, 17:28:23 via App

Evtl nimmst du mal den onKlick listener statt on touch listener...

LG Pascal //It's not a bug, it's a feature. :) ;)

Hilfreich?
Kommentieren
Fragensteller
  • Forum-Beiträge: 49

09.09.2018, 17:40:49 via Website

schon probiert, der klick landet in der warteschleife und wird erst ausgeführt, wenn tts fertig ist

Edit:

Blödes AS, habe ein paar mal Rebuild Project gedrückt und jetzt gehts.

— geändert am 09.09.2018, 18:14:58

Hilfreich?
Kommentieren