2010-12-18 22 views
15

Tengo problemas de codificación de caracteres extraños con una matriz JSON que se toma de una página web. El servidor está devolviendo este encabezado:Android Java UTF-8 HttpClient Problema

Content-Type text/javascript; charset = UTF-8

También puedo ver la salida JSON en Firefox o cualquier navegador y los caracteres Unicode se muestran correctamente. La respuesta a veces contendrá palabras de otro idioma con símbolos de acento y demás. Sin embargo, obtengo esos signos de interrogación raros cuando lo abro y lo pongo en una cadena en Java. Aquí está mi código:

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's 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(); 
} 

Como se puede ver, yo estoy especificando UTF-8 en el InputStreamReader pero cada vez que veo el texto JSON devuelto a través de la tostada que tiene signos de interrogación extraños. Estoy pensando que necesito enviar el InputStream a un byte [] en su lugar?

Gracias de antemano por cualquier ayuda.

Respuesta

37

Prueba esto:

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 
} 
+0

Gracias por la respuesta. Agregué los cambios e importé el material adicional de Apache para EntityUtils, pero ahora la aplicación solo termina inesperadamente en la línea EntityUtils.toString. programa se compila y se ejecuta, pero ¿tengo que hacer algo con la entidad antes de llamar a String? –

+0

no importa. Yo era un idiota y arruiné algo con mi url. ¡Funciona! ¡Los personajes se representan correctamente! –

+3

@Michael: Esta respuesta es muy buena y aceptaría esta si hubiera hecho la pregunta. – SK9

5

@ respuesta de Arhimed es la solución. Pero no puedo ver nada obviamente mal con su código convertStreamToString.

Mis conjeturas son:

  1. El servidor está poniendo un byte UTF marca de orden (BOM) al inicio de la corriente. El decodificador de caracteres Java UTF-8 estándar no elimina la lista de materiales, por lo que es probable que termine en la cadena resultante. (Sin embargo, el código para EntityUtils tampoco parece hacer nada con las listas de materiales).
  2. Su convertStreamToString lee el flujo de caracteres una línea a la vez, y lo vuelve a armar usando un cableado '\n' como el final de marcador de línea Si vas a escribir eso en un archivo o aplicación externo, probablemente deberías estar usando un marcador de fin de línea específico de la plataforma.
1

Es solo que su convertStreamToString no respeta la codificación establecida en HttpRespnose. Si mira dentro de EntityUtils.toString(entity, HTTP.UTF_8), verá que EntityUtils busca primero si hay una codificación establecida en la HttpResponse, luego si la hay, EntityUtils usa esa codificación. Solo recurrirá a la codificación pasada en el parámetro (en este caso HTTP.UTF_8) si no hay codificación establecida en la entidad.

Para que pueda decir que su HTTP.UTF_8 se pasa en el parámetro, pero nunca se utiliza porque es la codificación incorrecta. Así que aquí está la actualización de su código con el método de ayuda de 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's 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(); 
} 
0

La respuesta de Archimed es correcta. Sin embargo, esto se puede hacer simplemente proporcionando un encabezado adicional en la petición HTTP:

Accept-charset: utf-8 

No hay necesidad de eliminar cualquier cosa o utilizar cualquier otra biblioteca.

Por ejemplo,

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 

Lo más probable es que su petición no tiene ningún encabezado Accept-Charset.

0

Extraiga el juego de caracteres del campo de tipo de contenido de respuesta. Se puede utilizar el siguiente método para hacer esto:

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; 
} 

continuación, utilizar el juego de caracteres extraída para crear las InputStreamReader:

String charsetName = extractCharsetFromContentType(connection.getContentType()); 

InputStreamReader inReader = (TextUtils.isEmpty(charsetName) ? new InputStreamReader(inputStream) : 
        new InputStreamReader(inputStream, charsetName)); 
      BufferedReader reader = new BufferedReader(inReader); 
Cuestiones relacionadas