Проблема Java HttpClient для Java Java UTF-8
У меня возникают странные проблемы с кодировкой символов с массивом JSON, который захватывается с веб-страницы. Сервер отправляет обратно этот заголовок:
Content-Type text/javascript; кодировка = UTF-8
Также я могу посмотреть вывод JSON в Firefox или любой браузер, а символы Unicode отображаются правильно. В ответе иногда будут содержаться слова с другого языка с символами акцента и т.д. Однако я получаю эти странные вопросительные знаки, когда я вытаскиваю их и помещаю в строку на Java. Вот мой код:
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "utf-8");
params.setBooleanParameter("http.protocol.expect-continue", false);
HttpClient httpclient = new DefaultHttpClient(params);
HttpGet httpget = new HttpGet("http://www.example.com/json_array.php");
HttpResponse response;
try {
response = httpclient.execute(httpget);
if(response.getStatusLine().getStatusCode() == 200){
// Connection was established. Get the content.
HttpEntity entity = response.getEntity();
// If the response does not enclose an entity, there is no need
// to worry about connection release
if (entity != null) {
// A Simple JSON Response Read
InputStream instream = entity.getContent();
String jsonText = convertStreamToString(instream);
Toast.makeText(getApplicationContext(), "Response: "+jsonText, Toast.LENGTH_LONG).show();
}
}
} catch (MalformedURLException e) {
Toast.makeText(getApplicationContext(), "ERROR: Malformed URL - "+e.getMessage(), Toast.LENGTH_LONG).show();
e.printStackTrace();
} catch (IOException e) {
Toast.makeText(getApplicationContext(), "ERROR: IO Exception - "+e.getMessage(), Toast.LENGTH_LONG).show();
e.printStackTrace();
} catch (JSONException e) {
Toast.makeText(getApplicationContext(), "ERROR: JSON - "+e.getMessage(), Toast.LENGTH_LONG).show();
e.printStackTrace();
}
private static String convertStreamToString(InputStream is) {
/*
* To convert the InputStream to String we use the BufferedReader.readLine()
* method. We iterate until the BufferedReader return null which means
* there no more data to read. Each line will appended to a StringBuilder
* and returned as String.
*/
BufferedReader reader;
try {
reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
StringBuilder sb = new StringBuilder();
String line;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
Как вы можете видеть, я указываю UTF-8 на InputStreamReader, но каждый раз, когда я просматриваю возвращенный текст JSON через Toast, он имеет странные вопросительные знаки. Я думаю, что мне нужно отправить InputStream в байт [] вместо этого?
Заранее благодарим за помощь.
Ответы
Ответ 1
Попробуйте следующее:
if (entity != null) {
// A Simple JSON Response Read
// InputStream instream = entity.getContent();
// String jsonText = convertStreamToString(instream);
String jsonText = EntityUtils.toString(entity, HTTP.UTF_8);
// ... toast code here
}
Ответ 2
@Арговый ответ - это решение. Но я не вижу ничего явно неправильного с вашим кодом convertStreamToString
.
Мои догадки:
- В начале потока сервер помещает знак порядка байтов UTF (BOM). Стандартный дескриптор символов Java UTF-8 не удаляет спецификацию, поэтому есть вероятность, что он окажется в полученной String. (Тем не менее, код для EntityUtils, похоже, ничего не делает с спецификациями.)
- Ваш
convertStreamToString
читает поток символов по строке за раз и повторно собирает его с помощью проводного '\n'
в качестве маркера конца строки. Если вы собираетесь записать это во внешний файл или приложение, вероятно, вам следует использовать маркер конца конца в конкретной платформе.
Ответ 3
Просто ваш convertStreamToString не выполняет кодировку, установленную в HttpRespnose. Если вы заглянете внутрь EntityUtils.toString(entity, HTTP.UTF_8)
, вы увидите, что EntityUtils узнают, есть ли в HttpResponse первая кодировка, а если есть, EntityUtils использует эту кодировку. Он вернется только к кодировке, переданной в параметре (в данном случае HTTP.UTF_8), если в объекте нет кодировки.
Итак, вы можете сказать, что ваш HTTP.UTF_8 передается в параметре, но он никогда не используется, потому что это неправильная кодировка. Итак, вот обновление вашего кода с помощью вспомогательного метода от EntityUtils.
HttpEntity entity = response.getEntity();
String charset = getContentCharSet(entity);
InputStream instream = entity.getContent();
String jsonText = convertStreamToString(instream,charset);
private static String getContentCharSet(final HttpEntity entity) throws ParseException {
if (entity == null) {
throw new IllegalArgumentException("HTTP entity may not be null");
}
String charset = null;
if (entity.getContentType() != null) {
HeaderElement values[] = entity.getContentType().getElements();
if (values.length > 0) {
NameValuePair param = values[0].getParameterByName("charset");
if (param != null) {
charset = param.getValue();
}
}
}
return TextUtils.isEmpty(charset) ? HTTP.UTF_8 : charset;
}
private static String convertStreamToString(InputStream is, String encoding) {
/*
* To convert the InputStream to String we use the
* BufferedReader.readLine() method. We iterate until the BufferedReader
* return null which means there no more data to read. Each line will
* appended to a StringBuilder and returned as String.
*/
BufferedReader reader;
try {
reader = new BufferedReader(new InputStreamReader(is, encoding));
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
StringBuilder sb = new StringBuilder();
String line;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
Ответ 4
Архимадный ответ правильный. Однако это можно сделать просто путем предоставления дополнительного заголовка в HTTP-запросе:
Accept-charset: utf-8
Не нужно ничего удалять или использовать какую-либо другую библиотеку.
Например,
GET / HTTP/1.1
Host: www.website.com
Connection: close
Accept: text/html
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.10 Safari/537.36
DNT: 1
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: utf-8
Скорее всего, ваш запрос не имеет заголовка Accept-Charset
.
Ответ 5
Извлечь кодировку из поля типа содержимого ответа. Вы можете использовать следующий метод:
private static String extractCharsetFromContentType(String contentType) {
if (TextUtils.isEmpty(contentType)) return null;
Pattern p = Pattern.compile(".*charset=([^\\s^;^,]+)");
Matcher m = p.matcher(contentType);
if (m.find()) {
try {
return m.group(1);
} catch (Exception e) {
return null;
}
}
return null;
}
Затем используйте извлеченную кодировку для создания InputStreamReader
:
String charsetName = extractCharsetFromContentType(connection.getContentType());
InputStreamReader inReader = (TextUtils.isEmpty(charsetName) ? new InputStreamReader(inputStream) :
new InputStreamReader(inputStream, charsetName));
BufferedReader reader = new BufferedReader(inReader);