PDA

Ver la Versión Completa : TJsonArray con corchetes de más


jhonny
08-10-2015, 18:17:35
Buenos días, tardes, noches... a todos segun corresponda.

Hoy vengo a plantear un caso y saber si a ustedes les ha pasado esto y si lo han solucionado de alguna manera diferente.

Tengo Un DataSnap Server al cual me conecto por medio de su TDSHTTPService utilizando sus cualidades REST por medio de JSon.

En algun momento he necesitado compartir imagenes, archivos o en fin cualquier TStream, así que tengo una función en el server, parecida a esta, que devuelve el TStream serializado como TJsonArray, así:

function TServerMethods.GetImagen: TJSONArray;
var
vStream: TStream;
begin
vStream := TStream.Create;
try
vStream := GetImagenInterno;

Result := TDBXJSONTools.StreamToJSON(vStream, 0, vStream.Size);
finally
vStream.Free;
end;
end;

Básicamente lo que hace es cargar un archivo y devolverlo serializado en un TJsonArray de enteros, que debería verse así, por ejemplo:
[1, 5, 7, 8, 9...]

Pero por alguna razón el server envía dicho TJsonArray, así:
[[1, 5, 7, 8, 9...]]

Como pueden ver, por alguna razón por mí desconocida hasta el momento, agrega un corchete de más al inicio y un corchete de más, al final... por lo que al tratar de hacer lo siguiente en el cliente no convierte correctamente el arreglo a TStream:

var
vJArray: TJSONArray;
vMemStream: TMemoryStream;
begin
vMemStream := TMemoryStream.Create;
try
RESTRequest2.Execute;

vJArray := TJSONObject.ParseJSONValue(RESTResponse2.Content) as TJSONArray;

vMemStream.LoadFromStream(TDBXJSONTools.JSONToStream(vJArray)); //Con esto convierto de TJSonArray a TStream

ImageViewer1.Bitmap.LoadFromStream(vMemStream);

finally
vMemStream.Free;
end;
end;

Por lo que si hago una "chapuza" (Como la siguiente) para quitar de la cadena de texto recibida ese corchete incial y final de más... pues me muestra la imagen y todo aparenta estar bien, pero no es algo optimo ya que es un paso de más que finalmente afectará el rendimiento de la aplicación cliente:

var
vJArray: TJSONArray;
vMemStream: TMemoryStream;
vCadena: String;
begin
vMemStream := TMemoryStream.Create;
try
RESTRequest2.Execute;

vCadena := DMPrincipal.RESTResponse2.Content;
vCadena[1] := ' ';
vCadena[Length(vCadena)] := ' ';


vJArray := TJSONObject.ParseJSONValue(vCadena) as TJSONArray;

vMemStream.LoadFromStream(TDBXJSONTools.JSONToStream(vJArray)); //Con esto convierto de TJSonArray a TStream

ImageViewer1.Bitmap.LoadFromStream(vMemStream);

finally
vMemStream.Free;
end;
end;

La duda finalmente es, ¿Qué debería hacer para que el server me devuelva de manera natural y sin este tipo de "chapuzeras" correctamente el TJsonArray, sin esos corchetes de más?

Por su tiempo y atención, muchas gracias :)

roman
08-10-2015, 18:45:02
Ni idea. Pero más que pensar que te está insertando corchetes extra piensa que te está devolviendo un arreglo cuya única entrada es el arreglo que esperas. Quizá así tengas más idea de porqué lo hace y en lugar de quitar los corchetes extra simplemente toma el primer elemento del arreglo devuelto que, como dije, será el arreglo que esperabas.

// Saludos

jhonny
08-10-2015, 19:08:22
Ni idea. Pero más que pensar que te está insertando corchetes extra piensa que te está devolviendo un arreglo cuya única entrada es el arreglo que esperas. Quizá así tengas más idea de porqué lo hace y en lugar de quitar los corchetes extra simplemente toma el primer elemento del arreglo devuelto que, como dije, será el arreglo que esperabas.

// Saludos

Roman, definitivamente sos grande :D. Gracias por la luz he podido verlo de manera diferente... de momento el cliente ha quedado así:

var
vJArray: TJSONArray;
vMemStream: TMemoryStream;
begin
vMemStream := TMemoryStream.Create;
try
RESTRequest2.Execute;

vJArray := TJSONObject.ParseJSONValue(vCadena) as TJSONArray;

vMemStream.LoadFromStream(vMemStream.LoadFromStream
(TDBXJSONTools.JSONToStream(TJSONArray(vJArray.Items[0]))); //Con esto convierto de TJSonArray a TStream el arreglo que se encontraba en la posición 0

ImageViewer1.Bitmap.LoadFromStream(vMemStream);

finally
vMemStream.Free;
end;
end;

Ahora, a ver en el server el asunto, gracias de nuevo.

roman
08-10-2015, 19:21:04
¡Qué bueno que te haya servido! Para mi, esto que pones es contra natura Todo el delphi actual lo es :D

De entrada, veo DBX. ¿Eso se refiere a DBExpress? Y, si es así, ¿por qué DBExpress implementa métodos para convertir streams a formato JSon? :confused:

// Saludos

jhonny
08-10-2015, 21:38:45
¡Qué bueno que te haya servido! Para mi, esto que pones es contra natura Todo el delphi actual lo es :D

Bueno, es que analizando el asunto gracias a lo que me has comentado, me doy cuenta que todo valor de tipo JSon, el server DataSnap, lo devuleve como si de un TJsonValue se tratara, encerrandolo por ende dentro de esos corchetes, entonces de ahora en adelante, no debo pretender recibir un Json puro, sino un Json puro dentro de un TJsonValue.


De entrada, veo DBX. ¿Eso se refiere a DBExpress? Y, si es así, ¿por qué DBExpress implementa métodos para convertir streams a formato JSon? :confused:


Bueno, es que en principio el DataSnap está basado en bibliotecas DBExpress, pero a medida del tiempo han ido vinculando todas estas funcionalidades de REST, por lo que han ido vinculando nuevas unidades expecificas para tal uso, pero en algunos casos como en este siguen en aquellas unidades, supongo que por simple referencia. Aunque es sólo mi apreciación, es muy probable que esté equivocado.

roman
08-10-2015, 21:47:46
Bueno, es que en principio el DataSnap está basado en bibliotecas DBExpress, pero a medida del tiempo han ido vinculando todas estas funcionalidades de REST, por lo que han ido vinculando nuevas unidades expecificas para tal uso, pero en algunos casos como en este siguen en aquellas unidades, supongo que por simple referencia. Aunque es sólo mi apreciación, es muy probable que esté equivocado.

Claro, DBExpress evolucionó a DataSnap. Pero a lo que voy es a esto:

Tanto JSon, como streams son cosas independientes de DataSnap. Entonces, ¿qué pitos toca DataSnap implementando estas conversiones? No es su tarea.

Es decir, las bibliotecas de Delphi se han convertido en mounstruos hacelo-todo :rolleyes:

// Saludos

mamcx
08-10-2015, 22:23:26
devolverlo serializado en un TJsonArray de enteros, que debería verse así, por ejemplo:
[1, 5, 7, 8, 9...]


Oe, y no es mas logico devolver binarios usando base64??

jhonny
09-10-2015, 16:41:57
Claro, DBExpress evolucionó a DataSnap. Pero a lo que voy es a esto:

Tanto JSon, como streams son cosas independientes de DataSnap. Entonces, ¿qué pitos toca DataSnap implementando estas conversiones? No es su tarea.

Es decir, las bibliotecas de Delphi se han convertido en mounstruos hacelo-todo :rolleyes:

// Saludos

Bueno, los DBX implementan esas funciones para poder hacer conversiones entre JSon y sus respectivos valores, por ejemplo convertir un Arreglo Json a un DBXReader para luego poder leerlo como si se tratara de un DataSet (Nunca lo he hecho manualmente, pero supongo que así es). Entonces uno aprovecha que DBX tiene esas funciones para implementarlas en las demás cosas que necesita.

Oe, y no es mas logico devolver binarios usando base64??

Lo que es logico para unos, no es logico para los demás... en ese orden de ideas... ¿para ti, por qué es más logico?... ¿Compatibilidad, velocidad de transmisión, estandar, ninguna de las anteriores y/o todas las anteriores?, Delphi tiene sus funciones Encode y Decode en base 64, pero ya que no sé, ¿cual sería la razón logica para cambiar a binario base64?

mamcx
09-10-2015, 18:00:22
La razon es que ese es el medio mas usado para transmitir informacion binaria por http, y es un estándar en eso desde hace rato:

https://es.wikipedia.org/wiki/Base64

Ademas, estoy casi seguro (porque no se como seran tus datos ;) ) que debe ser mas pesado pasar por cada integer que en base64 (u otro encoding especializado en binarios), y es mas "natural", osea, un valor=un binario.

Ahora si asi te da ok. Puede que con compresion salga bien...

jhonny
11-10-2015, 07:23:39
La razon es que ese es el medio mas usado para transmitir informacion binaria por http, y es un estándar en eso desde hace rato:

https://es.wikipedia.org/wiki/Base64

Ademas, estoy casi seguro (porque no se como seran tus datos ;) ) que debe ser mas pesado pasar por cada integer que en base64 (u otro encoding especializado en binarios), y es mas "natural", osea, un valor=un binario.

Ahora si asi te da ok. Puede que con compresion salga bien...

Hola, buenas noches, acabo de hacer el cambio para comparar el asunto, no sólo desde el tema de compatibilidad, sino también para ver cual de los 2 metodos me sirve más en cuanto a rendimiento y transmisión de dichos datos... bueno, como bien indicabas me he decidido por utilizar Base64, pues no consume tanto performance del server despues de todo, es más comprimido y además es más utilizado... acá pongo el código del server en Base64, por si a alguien más le sirve.

De hecho Embarcadero recomienda usar el método anterior (TDBXJSONTools.JSONToStream) únicamente en llamadas asincronas y si el flujo de información no es muy alto.

function TServerMethods.GetFileBase64: String;
var
vStream: TStream;
vStringStream: TStringStream;
vBase64: TBase64Encoding;
begin
vStream := TStream.Create;
vStringStream := TStringStream.Create;
try
vStream := GetFileInterno; //Eso devuelve un TStream luego de comprimir el archivo en cuestión y hacer ciertas validaciones...

vBase64 := TBase64Encoding.Create;
vBase64.Encode(vStream, vStringStream);

Result := vStringStream.DataString;
finally
vStream.Free;
vStringStream.Free;
vBase64.Free;
end;
end;