[Ayuda]Problema al querer leer JSON hecho PHP desde Java

Iniciado por PabloPbl, 19 Octubre 2018, 02:37 AM

0 Miembros y 1 Visitante están viendo este tema.

PabloPbl

Hola buenas, estoy intentando obtener un JSON desde PHP, que me devuelve el resultado de una consulta (SELECT) de una base de datos.

Estoy usando GSON, para convertir el JSON obtenido a un objeto en Java.

Mi problema esta en que no me deja crear los objeto y me devuelve un Excepción.
Nose si lo que me devuelve PHP me devuelve en un formato correcto para poder ser interpretado en GSON.

Lo que si al momento de ver el JSON en consola para ver lo que me devuelve, me pone una especie de punto al inicio y otro al final, cosa que si se los saco anda perfectamente, pero me gustaría saber cual puede ser el error y que puedo estar haciendo mal.

MI PHP es el siguiente:
Código (php) [Seleccionar]

<?php
$servidor
="localhost";
$nombre_bd="test2";
$username="root";
$password="";

$conexion mysql_connect($servidor$username$password) or die ("No se ha podido conectar al servidor de Base de datos");

  
mysql_select_db($nombre_bd$conexion) or die ("xD");

$sql "SELECT * FROM usuario";

$result mysql_query($sql$conexion);

$array = array();

$i 0;

while(
$row mysql_fetch_array($result)) {
  
$array[$i] = $row;
  
$i ++;
}

$json json_encode($arrayJSON_FORCE_OBJECT);
echo 
$json;
?>



El Java de donde hago la petición:
Código (java) [Seleccionar]

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package prueba.comsumir.webservice;

import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

/**
*
* @author Pablo
*/
public class PruebaComsumirWebservice {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Usuario usuario = new Usuario(1, "User1", "123");
        System.out.println(usuario.toString());
        URL url;
        try {
            // Creando un objeto URL
            url = new URL("http://localhost:8080/Sitio%201/code.php");

            // Realizando la petición GET
            URLConnection con = url.openConnection();

            // Leyendo el resultado
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    con.getInputStream()));

            String linea;
            String json = "";
            while ((linea = in.readLine()) != null) {
                System.out.println(linea);
                json += linea;
            }
            //{"id":1,"usuario":"usuario1","contrasenia":"asd"}
            Usuario usuario1 = new Gson().fromJson(json, Usuario.class);
            System.out.println(usuario.getUsuario());
           
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
   


Y la clase Usuario:
Código (java) [Seleccionar]

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package prueba.comsumir.webservice;

/**
*
* @author Pablo
*/
public class Usuario {
    private int id;
    private String usuario;
    private String contrasenia;

    public Usuario(int id, String usuario, String contrasenia) {
        this.id = id;
        this.usuario = usuario;
        this.contrasenia = contrasenia;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsuario() {
        return usuario;
    }

    public void setUsuario(String usuario) {
        this.usuario = usuario;
    }

    public String getContrasenia() {
        return contrasenia;
    }

    public void setContrasenia(String contrasenia) {
        this.contrasenia = contrasenia;
    }

    @Override
    public String toString() {
        return String.format("id:%s, usuario:%s, contrasenia:%s", id, usuario, contrasenia);
    }
}



Cuando ejecuto el código desde Netbeans, me devuelve el siguiente JSON: y me da la siguiente Excepción:

{"0":{"0":"1","id":"1","1":"user1","usuario":"user1","2":"asd","contrasenia":"asd"},"1":{"0":"2","id":"2","1":"user2","usuario":"user2","2":"dsa","contrasenia":"dsa"}}
Exception in thread "main" com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 169 path $
   at com.google.gson.Gson.assertFullConsumption(Gson.java:855)
   at com.google.gson.Gson.fromJson(Gson.java:845)
   at com.google.gson.Gson.fromJson(Gson.java:793)
   at com.google.gson.Gson.fromJson(Gson.java:765)
   at prueba.comsumir.webservice.PruebaComsumirWebservice.main(PruebaComsumirWebservice.java:48)
Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 169 path $
   at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1567)
   at com.google.gson.stream.JsonReader.checkLenient(JsonReader.java:1416)
   at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:546)
   at com.google.gson.stream.JsonReader.peek(JsonReader.java:429)
   at com.google.gson.Gson.assertFullConsumption(Gson.java:851)
   ... 4 more
C:\Users\Pablo\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 1 second)


Un saludo y gracias desde ya.

rub'n

#1
Hola,

es de caracter privado lo que tiene tu json ? http://localhost:8080/Sitio%201/code.php

puedo verlo? por lo visto es el JSON que estas generando


rubn0x52.com KNOWLEDGE  SHOULD BE FREE!!!
If you don't have time to read, you don't have the time (or the tools) to write, Simple as that. Stephen

WHK

#2
Hola, mira:


Pegando el código json:
{"0":{"0":"1","id":"1","1":"user1","usuario":"user1","2":"asd","contrasenia":"asd"},"1":{"0":"2","id":"2","1":"user2","usuario":"user2","2":"dsa","contrasenia":"dsa"}}

Acá: http://json.parser.online.fr/

CitarSyntaxError: JSON.parse: unexpected non-whitespace character after JSON data at line 1 column 168 of the JSON data

He copiado exactamente tu código y me he dado cuenta que hay un caracter especial invisible al final que está haciendo que el código json sea corrupto, especiifcamente son 3 bytes: %EF%BB%BF.

whk@machine:~$ echo '{"0":{"0":"1","id":"1","1":"user1","usuario":"user1","2":"asd","contrasenia":"asd"},"1":{"0":"2","id":"2","1":"user2","usuario":"user2","2":"dsa","contrasenia":"dsa"}}' | xxd
00000000: 7b22 3022 3a7b 2230 223a 2231 222c 2269  {"0":{"0":"1","i
00000010: 6422 3a22 3122 2c22 3122 3a22 7573 6572  d":"1","1":"user
00000020: 3122 2c22 7573 7561 7269 6f22 3a22 7573  1","usuario":"us
00000030: 6572 3122 2c22 3222 3a22 6173 6422 2c22  er1","2":"asd","
00000040: 636f 6e74 7261 7365 6e69 6122 3a22 6173  contrasenia":"as
00000050: 6422 7d2c 2231 223a 7b22 3022 3a22 3222  d"},"1":{"0":"2"
00000060: 2c22 6964 223a 2232 222c 2231 223a 2275  ,"id":"2","1":"u
00000070: 7365 7232 222c 2275 7375 6172 696f 223a  ser2","usuario":
00000080: 2275 7365 7232 222c 2232 223a 2264 7361  "user2","2":"dsa
00000090: 222c 2263 6f6e 7472 6173 656e 6961 223a  ","contrasenia":
000000a0: 2264 7361 227d 7def bbbf 0a              "dsa"}}....


ef bbbf, el 0a es el salto de línea que hace echo en el bash.

Estos tres bytes corresponden a la cabecera de una codificación de carácteres de utf-8 con bom, el cual no es compatible con json, solo es compatible con utf-8 simple.

Tu código php o tu clase en java está agregando mas bytes de los que corresponden. Normalmente puedes solucionarlo haciendo un substring eliminando los últimos 3 carácteres, pero en ves de eso te recomiendo buscar la fuente que está generando esos bytes y los elimines.

Como tip: puedes utilizar la palabra "contraseña" con eñe, siempre y cuando esté codificado en utf-8.

¿Donde están escrito esos bytes?: Esos bytes son de cabecera de archivo, probablemente estés utilizando un editor de texto el cual configuraste utf-8 con bom, eso debes cambiarlo a utf-8 simple, segundo... si es una cabecera de archivo ¿porqué aparece al final del json?: probablemente porque después de arrojar el código json hace algún tipo de inclusión a un archivo php el cual tiene esos caracteres basura al comienzo del script antes de la apertura del tag, por ejemplo:

app.php
<?php ... mostrar_json(); ... include('dañado.php'); 

dañado.php
%EF%BB%BF<?php mi código acá ...

Eso pasa mucho al no considerar la codificación de caracteres en un proyecto. Búsca en todos tus archivos de php y desde el mismo editor reemplaza la codificación a utf-8 y aparecerán caracteres basura antes de las etiquetas php, es un trabajo de limpieza que tendrás que hacer si es el caso y de pasada ir reemplazando los acentos y eñes.

Nota: Te has fijado que en tu mismo código php que has pegado en tu post, al final aparece esto?: "&#65279;", ¿qué hace ahi?, claramente el foro lo ha escapado automáticamente, pero es un caracter especial que se encuentra en tu código php.

Recomendación adicional: Si utilizas un script en php que no va a utilizar código html, como es tu caso, entonces no escribas el cierre de etiqueta, elimina las últimas líneas de tu script y ya, php solo necesita el tag de apertura, no el de cierre, eso evita muchos problemas con el escape de bytes sobrantes y el orden del envío de cabeceras http v/s el body.

También te recomiendo que tu código java sea capaz de saber si no se puede conectar al servicio, si el json es corrupto, si los datos no son correctos, etc.

Otras recomendaciones adicionales:

mysql_select_db($nombre_bd, $conexion) or die ("xD");

Acá debiera retornar código json indicando el estado fallido de la obtención de datos, sino, cómo sabrá tu código java que hubo un problema?, tendrías que procesar múltipes tipos de cabeceras. Recuerda arrojar una cabecera de tipo application/json:

header('Content-type: application/json; charset=utf-8');

También he visto que usas datos numéricos como llaves de objetos, cómo sabrás la cantidad y tipo?, en ves de eso te recomiendo que los transformes en arrays y nunca valores numéricos como llaves de objetos, eso lo vas a lograr eliminando la propiedad "JSON_FORCE_OBJECT" de tu json_encode. Si deseas transformar tu array de datos a un objeto manualmente puedes hacer esto:

$array[$i] = (object)$row;

Y te va a resultar mucho mejor la asociación de llaves si usas mysql_fetch_assoc.

Otra cosa, recuerda utilizar mysqli en ves de mysql y tener instalada tu librería mysqlnd.

Ahora si, saludos.

WHK

#3
Mira, he limpiado un poco tu código para solucionar algunos problemas:

Código (php) [Seleccionar]
<?php

class Wservice
{
    private 
$status;
    private 
$mysql_data;
    private 
$mysql_handler;

    public function 
__construct()
    {
        
$this->mysql_data = array(
            
'host'     => '127.0.0.1',
            
'dbname'   => 'test2',
            
'user'     => 'root',
            
'password' => ''
        
);

        
$this->mysql_handler = new mysqli(
            
$this->mysql_data['host'],
            
$this->mysql_data['user'],
            
$this->mysql_data['password'],
            
$this->mysql_data['dbname']
        );

        if(
$this->mysql_handler)
        {
            
$this->status 1// Success
        
}
        else
        {
            
$this->status = -1// Unable connect to database
        
}
    }

    public function 
__destruct()
    {
        
// Close connection
        
if($this->mysql_handler)
        {
            
$this->mysql_handler->close();
        }
    }

    private function 
getData()
    {
        
$data = array();

        
$result_handler $this->mysql_handler->query('
            SELECT *
            FROM usuarios -- Plural!
            -- LIMIT ???? Exhaust memory vulnerability!
        '
);
        if(
$result_handler)
        {
            while(
$row $result_handler->fetch_assoc())
            {
                
$data[] = $row;
            }

            
$result_handler->free();
        }
        else
        {
            
$this->status = -2// Query fail
        
}

        
// query fail? return a empty array (prevent exceptions in java)
        
return $data;
    }

    public function 
out()
    {
        
// Set the content type
        
header('Content-type: application/json');

        
// Prevent cache
        
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
        
header('Cache-Control: post-check=0, pre-check=0'false);
        
header('Pragma: no-cache');

        
// Get required data
        
$data $this->getData();

        
// Out the data and status
        
echo json_emcode(array(
            
'status' => $this->status,
            
'data'   => $data
        
));
    }
}

$wservice Wservice();
$wservice->out();


El estado 1 indica que todo resultó ok, el -1 es porque falló la conexión a la base de datos y -2 es porque falló la consulta a la base de datos.

Recuerda darle un límite a tu consulta sql, porque si hay un millón de usuarios no podrás devolver un json con un millón de registros. Averigua sobre técnicas de scafollding, eso quiere decir que sería bueno que hagas un servicio rest para buscar un solo usuario, buscar varios usuarios con un límite (buscador), uno que elimine el usuario y otro que edite sus datos, ese orden te ayudará mucho en tu proyecto con separación de capas (como veo que lo estás haciendo). También te ayudaría mucho el uso de microservicios utilizando docker para aumentar la escalabilidad de tu proyecto ya que veo que has separado las funcionalidades en servicios rest independientes.

Recuerda desactivar los mensajes de error de php o si no cuando falle algo te arrojará un warning en html dentro de una cabecera json y todo estallará y volará por los aires destruyendo todo a su paso xD

Saludos.