Преобразование строки в Android JSONObject теряет utf-8

Я пытаюсь получить строку (в формате JSON) из URL-адреса и использовать ее как объект Json. Я теряю кодировку UTF-8, когда конвертирую строку в JSONObject.

Это функция, которую я использую для подключения к URL-адресу и получения строки:

private static String getUrlContents(String theUrl) {
    StringBuilder content = new StringBuilder();
    try {
        URL url = new URL(theUrl);
        URLConnection urlConnection = url.openConnection();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));

        String line;
        while ((line = bufferedReader.readLine()) != null) {
            content.append(line + "\n");
        }
        bufferedReader.close();
    } catch(Exception e) {
        e.printStackTrace();
    }

    return content.toString();
}

Когда я получаю данные с сервера, следующий код отображает правильные символы:

String output = getUrlContents(url);
Log.i("message1", output);

Но когда я конвертирую выходную строку в JSONObject, персидские символы становятся вопросительными знаками, такими как ??????. (messages — это имя массива в JSON)

JSONObject reader = new JSONObject(output);
String messages = new String(reader.getString("messages").getBytes("ISO-8859-1"), "UTF-8");
Log.i("message2", messages);

person Ali Sheikhpour    schedule 08.01.2016    source источник


Ответы (4)


Вы говорите Java преобразовать строку (с ключом message) в байты, используя ISO-8859-1, а затем создать новую строку из этих байтов, интерпретируемую как UTF-8.

new String(reader.getString("messages").getBytes("ISO-8859-1"), "UTF-8");

Вы можете просто использовать:

String messages = reader.getString("messages");
person toKrause    schedule 11.01.2016
comment
Это работает, потому что байты, которые вы получаете по сети, уже правильно интерпретируются в getUrlContents и внутренне хранятся в виде строки UTF-16. - person toKrause; 11.01.2016
comment
getUrlContents работает только тогда, когда кодировка сервера совпадает с кодировкой клиента. - person Alastair McCormack; 16.01.2016

Вы можете обновить свой код следующим образом:

    private static String getUrlContents(String theUrl) {
        StringBuilder content = new StringBuilder();
        try {
            URL url = new URL(theUrl);
            URLConnection urlConnection = url.openConnection();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "utf-8"));

            String line;
            while ((line = bufferedReader.readLine()) != null) {
                content.append(line).append("\n");
            }
            bufferedReader.close();
        } catch(Exception e) {
            e.printStackTrace();
        }

        return content.toString().trim();
    }
person BNK    schedule 12.01.2016

У вас есть две проблемы с кодировкой:

  1. Сервер отправляет текст, закодированный в наборе символов. Когда вы настраиваете InputStreamReader, вам нужно передать кодировку, используемую сервером, чтобы ее можно было правильно декодировать. Кодировка символов обычно указывается в HTTP-ответе Content-type в поле charset. JSON обычно имеет кодировку UTF-8, но также может быть юридически UTF-16 и UTF-32, поэтому вам нужно проверить. Без указанной кодировки ваша системная среда будет использоваться при маршаллинге байтов в строки и наоборот. По сути, вы всегда должны указывать кодировку.

  2. String messages = new String(reader.getString("messages").getBytes("ISO-8859-1"), "UTF-8");, очевидно, вызовет проблемы (если у вас есть символы, отличные от ascii) - он кодирует строку в ISO-8995-1, а затем пытается декодировать ее как UTF-8.

Можно использовать простой шаблон регулярного выражения для извлечения значения charset из заголовка Content-type перед чтением входного потока. Я также включил аккуратный конвертер InputStream -> String.

private static String getUrlContents(String theUrl) {

    try {
        URL url = new URL(theUrl);
        URLConnection urlConnection = url.openConnection();
        InputStream is = urlConnection.getInputStream();

        // Get charset field from Content-Type header
        String contentType = urlConnection.getContentType();
        // matches value in key / value pair
        Pattern encodingPattern = Pattern.compile(".*charset\\s*=\\s*([\\w-]+).*");
        Matcher encodingMatcher = encodingPattern.matcher(contentType);
        // set charsetString to match value if charset is given, else default to UTF-8
        String charsetString = encodingMatcher.matches() ? encodingMatcher.group(1) : "UTF-8";

        // Quick way to read from InputStream.
        // \A is a boundary match for beginning of the input
        return new Scanner(is, charsetString).useDelimiter("\\A").next();
    } catch(Exception e) {
        e.printStackTrace();
    }

    return null;
}
person Alastair McCormack    schedule 12.01.2016

Не уверен, что это поможет, но вы можете сделать что-то вроде этого:

JSONObject result = null;
String str = null;
try 
{           
    str = new String(output, "UTF-8");
    result = (JSONObject) new JSONTokener(str).nextValue();
} 
catch (Exception e) {}

String messages = result.getString("messages");
person jt-gilkeson    schedule 11.01.2016