· 6 years ago · Nov 12, 2019, 10:08 AM
1/**
2 * ====================================================================================================
3 *
4 * FUNCIONES COMUNES para el API REST v1
5 *
6 * 1.01 - 15/06/2016 - Modificadas funciones tablaIdRef(), objetosTipoTablaDestino() y tablaDestinoIdRef()
7 * 1.02 - 31/10/2016 - Revisada la función imagenFromBase64 para procesar devolver imágenes recibidasen base64
8 * 1.03 - 25/09/2017 - Revisado estilo de llaves
9 * 1.04 - 26/02/2019 - Permitir que en la URL los parámetros puedan contener el símbolo ?
10 * 1.05 - 11/11/2019 - Optimizamos search con indexOf y contemplar api_key que nos llegue por headers
11 *
12 * ====================================================================================================
13 **/
14
15importClass("VImage");
16
17
18/**
19 * ----------------------------------------------------------------------------------------------------
20 * requestToObjeto [Parseador de la request, devuelve un objeto JSON con todas las partes extraídas]
21 *
22 * @param {[String]} url [Requerido URL que se parseará para extraer todas sus partes]
23 * @return {Object} requestParsed Objeto JSON con todas las variables y arrays de las partes
24 * ----------------------------------------------------------------------------------------------------
25 **/
26function requestToObjeto() {
27
28 // Extraemos las partes del bloque pathInfo del request
29 var uriMetodo = theRequest.method();
30 var uriPathInfo = theRequest.pathInfo();
31 var uriPathInfoSplit = uriPathInfo.split('/');
32 var posicionVersion = uriPathInfoSplit.indexOf(version);
33 var uriRecurso = (uriPathInfoSplit.length > (posicionVersion + 0)) ? uriPathInfoSplit[posicionVersion + 1] : "";
34 var uriIdentificador = (uriPathInfoSplit.length > (posicionVersion + 1)) ? uriPathInfoSplit[posicionVersion + 2] : "";
35 var uriRelaciones = (uriPathInfoSplit.length > (posicionVersion + 2)) ? uriPathInfoSplit[posicionVersion + 3] : "";
36 var uriRecursosrel = (uriPathInfoSplit.length > (posicionVersion + 3)) ? uriPathInfoSplit[posicionVersion + 4] : "";
37
38 // Preparamos el idRef del recurso: tabla, proceso o búsqueda
39 var uriProceso = "";
40 var uriBusqueda = "";
41 var uriTabla = "";
42
43 switch (uriRecurso) {
44 case "_process":
45 var uriProceso = objetoIdRef(VObjectInfo.TypeProcess, uriIdentificador.toUpperCase());
46 if (uriProceso) {
47 var uriTabla = tablaDestinoIdRef(VObjectInfo.TypeProcess, uriProceso);
48 }
49 break;
50
51 case "_query":
52 var uriBusqueda = objetoIdRef(VObjectInfo.TypeQuery, uriIdentificador.toUpperCase());
53 if (uriBusqueda) {
54 var uriTabla = tablaDestinoIdRef(VObjectInfo.TypeQuery, uriBusqueda);
55 }
56 break;
57
58 default:
59 var uriTabla = tablaIdRef(uriRecurso.toUpperCase());
60 }
61
62 // Extraemos los parámetros del request
63 var uriUnparsedUri = decodeURIComponent(theRequest.unparsedUri());
64 var uriParametros = uriUnparsedUri.substring(uriUnparsedUri.indexOf('?',0)+1,uriUnparsedUri.length);
65
66 // Creamos los arrays de los diferentes tipos de parámetros
67 var uriApiKey = [];
68 var uriErrors = [];
69 var uriFields = [];
70 var uriFieldsTablas = [];
71 var uriFieldsCampos = [];
72 var uriFilter = [];
73 var uriInclude = [];
74 var uriPage = [];
75 var uriParam = [];
76 var uriSort = [];
77
78 // Extraemos los diferentes tipos de parámetros
79 if (uriParametros != undefined) {
80 var uriParametrosSplit = uriParametros.split('&');
81 for (var index = 0; index < uriParametrosSplit.length; index++) {
82 var parametro = uriParametrosSplit[index];
83 var parametroLower = parametro.toLowerCase();
84
85 if (parametroLower.indexOf("api_key") != -1) {
86 uriApiKey.push(parametro.replace("api_key=", ""));
87 } else if (parametroLower.indexOf("fields") != -1) {
88 uriFields.push(parametro.replace("fields", ""));
89 } else if (parametroLower.indexOf("filter") != -1) {
90 uriFilter.push(parametro.replace("filter", ""));
91 } else if (parametroLower.indexOf("include") != -1) {
92 uriInclude.push(parametro.replace("include=", ""));
93 } else if (parametroLower.indexOf("page") != -1) {
94 uriPage.push(parametro.replace("page", ""));
95 } else if (parametroLower.indexOf("param") != -1) {
96 uriParam.push(parametro.replace("param", ""));
97 } else if (parametroLower.indexOf("sort") != -1) {
98 uriSort.push(parametro.replace("sort=", ""));
99 }
100 }
101 }
102
103 // Si se han recibido fields se repasan creando dos arrays uno con las tablas y otro con la lista de campos de la tabla
104 if (uriFields.length > 0) {
105 for (var index = 0; index < uriFields.length; index++) {
106 var fieldsSplit = uriFields[index].split('=');
107 if (fieldsSplit[0] == "") {
108 uriFieldsTablas.push(uriTabla.split("/")[1].toLowerCase());
109 } else {
110 uriFieldsTablas.push(fieldsSplit[0].substring(1, fieldsSplit[0].length - 1).toLowerCase());
111 }
112 uriFieldsCampos.push(fieldsSplit[1]);
113 }
114 }
115
116 // Si no hemos recibido información sobre paginación asumimos un máximo de 1000 registros a devolver
117 if (uriPage.length == 0) {
118 uriPage.push("[number]=1");
119 uriPage.push("[size]=1000");
120 }
121
122 // 11/11/2019 - Ismael Serrano - Comprobamos si tenemos api_key en headers
123 var xApiKey = typeof theRequest.header("X-API-Key") !== "undefined" ? theRequest.header("X-API-Key") : false;
124 if (uriApiKey.length == 0 && xApiKey !== false){
125 uriApiKey.push(xApiKey);
126 }
127
128 // Leemos el body del POST
129 var uriBody = "";
130 if (uriMetodo == "POST") {
131 if (esValidoJson(theRequest.body())) {
132 uriBody = JSON.parse(theRequest.body()); // no hace falta volver a definir otra vez la variable
133 } else {
134 uriErrors.push("El objeto JSON recibido en el body de la petición no es válido");
135 }
136 }
137
138 // Preparar objeto de retorno
139 var uriObjeto =
140 {
141 'api_key' : uriApiKey,
142 'metodo' : uriMetodo,
143 'recurso' : uriRecurso,
144 'tabla' : uriTabla,
145 'proceso' : uriProceso,
146 'busqueda' : uriBusqueda,
147 'identificador' : uriIdentificador,
148 'relaciones' : uriRelaciones,
149 'recursosrel' : uriRecursosrel,
150 'fields_tablas' : uriFieldsTablas,
151 'fields_campos' : uriFieldsCampos,
152 'filter' : uriFilter,
153 'include' : uriInclude,
154 'page' : uriPage,
155 'param' : uriParam,
156 'sort' : uriSort,
157 'body' : uriBody,
158 'errores' : uriErrors
159 };
160
161 // Obtenemos la información de seguridad del API Key y la añadimos a uriObjeto
162 getApiKeySeguridad(uriObjeto);
163
164 // Devolver el objeto con todos los datos parseados
165 return uriObjeto;
166}
167
168
169/**
170 * ----------------------------------------------------------------------------------------------------
171 * parseRequest [Parseador de la request, devuelve un objeto JSON con todas las partes extraídas]
172 *
173 * @param {[Object]} uriObjeto []
174 * @return {Object} requestParsed Objeto JSON con todas las variables y arrays de las partes
175 * ----------------------------------------------------------------------------------------------------
176 **/
177function getApiKeySeguridad(uriObjeto) {
178
179 // Preparar las variables de trabajo
180 var uriSegMetodos = [];
181 var uriSegCampos = [];
182 var uriSegProcesos = [];
183 var uriSegBusquedas = [];
184
185 // Leemos el registro del API Key para obtener su código (ID)
186 var registroApiKey = new VRegister(theRoot);
187 registroApiKey.setTable("velneo_verp_2_dat/API_KEY_W");
188 registroApiKey.readRegister("API_KEY", [uriObjeto.api_key[0]], VRegister.SearchThis);
189 var apikeyId = registroApiKey.fieldToInt("ID");
190
191 if (apikeyId != 0) {
192 // Leemos el registro de la seguridad para el API Key
193 var registroSeg = new VRegister(theRoot);
194 registroSeg.setTable("velneo_verp_2_dat/API_SEG_W");
195 var indice = ( uriObjeto.tabla == "" ? "API_KEY_SIN_TAB" : "API_KEY_TAB" );
196 var partes = [];
197 partes.push(apikeyId);
198 if (uriObjeto.tabla != "") { partes.push(uriObjeto.tabla); }
199
200 if (registroSeg.readRegister(indice, partes, VRegister.SearchThis) == true) {
201 // Leemos los métodos admitidos
202 if (registroSeg.fieldToBool("MET_GET") == true) { uriSegMetodos.push("GET"); }
203 if (registroSeg.fieldToBool("MET_PUT") == true) { uriSegMetodos.push("PUT"); }
204 if (registroSeg.fieldToBool("MET_POS") == true) { uriSegMetodos.push("POST"); }
205 if (registroSeg.fieldToBool("MET_DEL") == true) { uriSegMetodos.push("DELETE"); }
206
207 // -------------------
208 // Seguridad de CAMPOS
209 // -------------------
210 // Leemos los campos admitidos, si no están marcados todos esos son los admitidos en caso de estar marcados todos pasan a ser los excluidos
211 apikeyCampos = registroSeg.fieldToString("CAM").split(",");
212 var campos = [];
213 if (registroSeg.fieldToBool("CAM_TOD") == true) {
214 campos = camposTabla(uriObjeto.tabla, apikeyCampos);
215 } else {
216 if (apikeyCampos.length > 0) { campos = apikeyCampos; }
217 }
218
219 // Si hay parámetro fields para la tabla se procesan sus campos para dejar solo esos en la lista de campos a retornar
220 var campos_tablas = uriObjeto.fields_tablas;
221 if (campos_tablas.length) {
222 var posicionTabla = campos_tablas.indexOf(uriObjeto.tabla.split("/")[1].toLowerCase());
223 if (posicionTabla != -1) {
224 var fieldsCamposId = uriObjeto.fields_campos[posicionTabla].split(",");
225 for (var numCampo = 0; numCampo < fieldsCamposId.length; numCampo++) {
226 if (campos.indexOf(fieldsCamposId[numCampo].split(".")[0].toUpperCase()) != -1) {
227 uriSegCampos.push(fieldsCamposId[numCampo].toUpperCase());
228 } else {
229 uriObjeto.errores.push("No se retornan valores del campo " + fieldsCamposId[numCampo]);
230 }
231 }
232 }
233 }
234 else {
235 uriSegCampos = campos;
236 }
237
238 // ---------------------
239 // Seguridad de PROCESOS
240 // ---------------------
241 // Leemos los procesos admitidos, si no están marcados todos esos son los admitidos en caso de estar marcados todos pasan a ser los excluidos
242 apikeyProcesos = registroSeg.fieldToString("PRO").split(",");
243 if (registroSeg.fieldToBool("PRO_TOD") == true) {
244 uriSegProcesos = objetosTipoTablaDestino(VObjectInfo.TypeProcess, uriObjeto.tabla, apikeyProcesos);
245 } else {
246 if (apikeyProcesos.length > 0) { uriSegProcesos = apikeyProcesos; }
247 }
248
249 // ----------------------
250 // Seguridad de BUSQUEDAS
251 // ----------------------
252 // Leemos las búsquedas admitidas, si no están marcadas todas esas son las admitidas en caso de estar marcadas todas pasan a ser las excluidas
253 apikeyBusquedas = registroSeg.fieldToString("BUS").split(",");
254 if (registroSeg.fieldToBool("BUS_TOD") == true) {
255 uriSegBusquedas = objetosTipoTablaDestino(VObjectInfo.TypeQuery, uriObjeto.tabla, apikeyBusquedas);
256 } else {
257 if (apikeyBusquedas.length > 0) { uriSegBusquedas = apikeyBusquedas; }
258 }
259 }
260 } else {
261 uriObjeto.errores.push("El API Key de la solicitud no es válido");
262 }
263
264 // Añadimos los datos de seguridad al objeto
265 uriObjeto.seg_metodos = uriSegMetodos;
266 uriObjeto.seg_campos = uriSegCampos;
267 uriObjeto.seg_procesos = uriSegProcesos;
268 uriObjeto.seg_busquedas = uriSegBusquedas;
269}
270
271
272/**
273 * ----------------------------------------------------------------------------------------------------
274 * valorCampoJSON [Devuelve el valor de un campo en formato JSON]
275 *
276 * @param {[VRegister]} registro Registro del que se obtendrá el valor del campo
277 * @param {[Number]} campo Numero de campo del registro
278 * @returns {String} Devuelve una cadena de texto con el valor del campo en formato JSON
279 * ----------------------------------------------------------------------------------------------------
280 **/
281function valorCampoJSON(registro, campoId, campoTipo) {
282
283 // Preparamos las variables de trabajo
284 var valor = "";
285
286 // Obtenemos el valor en función del tipo de campo
287 switch(campoTipo) {
288 case VTableInfo.FieldTypeDateTime: // Campos de tipo tiempo (fecha y hora)
289 valor = registro.fieldToDateTime(campoId).toJSON();
290 break;
291
292 case VTableInfo.FieldTypeTime: // Campos de tipo hora
293 valor = registro.fieldToTime(campoId).toString();
294 break;
295
296 case VTableInfo.FieldTypeDate: // Campos de tipo fecha
297 // Componemos una nueva fecha UTC ya que la del campo viene con la diferencia horaria local
298 var fechaJS = new Date();
299 var fecha = registro.fieldToDate(campoId);
300 fechaJS.setTime(Date.UTC(fecha.getFullYear(), fecha.getMonth(), fecha.getDate()));
301 valor = fechaJS.toJSON();
302 break;
303
304 case VTableInfo.FieldTypeBool: // Campos de tipo booleano
305 valor = (registro.fieldToInt(campoId) == 1) ? true : false;
306 break;
307
308 case VTableInfo.FieldTypeNumeric: // Campos de tipo numérico
309 valor = registro.fieldToDouble(campoId);
310 break;
311
312 case VTableInfo.FieldTypeObject: // Campos de tipo objeto
313 var campoNumero = registro.tableInfo().findField(campoId);
314 var campoTipoObjeto = registro.tableInfo().fieldObjectType(campoNumero);
315 switch(campoTipoObjeto)
316 {
317 case VTableInfo.ObjectTypeText: // Objeto texto
318 case VTableInfo.ObjectTypeRichText: // Objeto texto enriquecido
319 case VTableInfo.ObjectTypeFormula: // Objeto fórmula
320 valor = registro.fieldToString(campoId);
321 break;
322
323 case VTableInfo.ObjectTypePicture: // Objeto dibujo
324 var imagen = registro.fieldToImage(campoId);
325 if (imagen)
326 valor = imagenToBase64(imagen);
327 break;
328 }
329 break;
330
331 default: // El resto de tipos de campos
332 valor = registro.fieldToString(campoId);
333 }
334
335 // Retornamos el valor del campo
336 return valor;
337}
338
339
340/**
341 * ----------------------------------------------------------------------------------------------------
342 * tablaIdRef [Devuelve el idRef del identificador de una tabla]
343 *
344 * @param {[String]} tablaId [Requerido Identificador de la tabla]
345 * @return {String} idRef de la tabla
346 * ----------------------------------------------------------------------------------------------------
347 **/
348function tablaIdRef(tablaId) {
349
350 // Se lee el proyecto principal
351 var proyecto = theApp.mainProjectInfo();
352
353 // Se repasan todas las tablas buscando la recibida en el parámetro
354 for (var numTabla = 0; numTabla < proyecto.allTableCount(); numTabla++) {
355 if (proyecto.allTableInfo(numTabla).id() == tablaId) {
356 return proyecto.allTableInfo(numTabla).idRef();
357 }
358 }
359
360 // Si no se ha encontrado se devuelve null
361 return null;
362}
363
364
365/**
366 * ----------------------------------------------------------------------------------------------------
367 * objetoIdRef [Devuelve el idRef del identificador de un objeto]
368 *
369 * @param {[String]} objetoTipo [Requerido Tipo de objeto]
370 * @param {[String]} objetoId [Requerido Identificador del objeto]
371 * @return {String} idRef del objeto
372 * ----------------------------------------------------------------------------------------------------
373 **/
374function objetoIdRef(objetoTipo, objetoId) {
375
376 // Se lee el proyecto principal
377 var proyecto = theApp.mainProjectInfo();
378
379 // Se repasan todos los objetos tablas buscando la recibida en el parámetro
380 for (var numObjeto = 0; numObjeto < proyecto.allObjectCount(objetoTipo); numObjeto++) {
381 if (proyecto.allObjectInfo(objetoTipo, numObjeto).id() == objetoId) {
382 return proyecto.allObjectInfo(objetoTipo, numObjeto).idRef();
383 }
384 }
385
386 // Si no se ha encontrado se devuelve null
387 return null;
388}
389
390
391/**
392 * ----------------------------------------------------------------------------------------------------
393 * tablaDestinoIdRef [Devuelve el idRef de la tabla de destino de un objeto]
394 *
395 * @param {[String]} objetoTipo [Requerido Tipo de objeto]
396 * @param {[String]} objetoIdRef [Requerido Identificador de referencia del objeto]
397 * @return {String} idRef de la tabla de destino del objeto
398 * ----------------------------------------------------------------------------------------------------
399 **/
400function tablaDestinoIdRef(objetoTipo, objetoIdRef) {
401
402 var tablaDestinoIdRef = "";
403
404 // Obtenemos el proyecto
405 var objetoProyecto = theApp.projectInfo(objetoIdRef.split("/")[0]);
406 if (objetoProyecto) {
407 // Obtenemos el objectInfo del objeto
408 var objetoId = objetoIdRef.split("/")[1];
409 var objetoInfo = objetoProyecto.objectInfo(objetoTipo, objetoId);
410 if (objetoInfo) {
411 // Analizamos la salida del objeto
412 if (objetoInfo.outputType() != VObjectInfo.IONone) {
413 tablaDestinoIdRef = objetoInfo.outputTable().idRef();
414 }
415 }
416 }
417
418 // Retornamos el idRef de la tabla de destino del objeto
419 return tablaDestinoIdRef;
420}
421
422
423/**
424 * ----------------------------------------------------------------------------------------------------
425 * Lista objetos de tipo y tabla de destino [Devuelve array con idRefs de objetos de un tipo y tabla destino]
426 *
427 * @param {[String]} tipoObjeto [Requerido tipo de objeto]
428 * @param {[String]} tablaDestino [Requerido idRef de la tabla de destino de los objetos]
429 * @param {[Array]} objetosExcluir [Requerido Array de idRefs de los obtejos a excluir de la lista a retornar]
430 * @return [{String}] objetosIdRef [Array de idRefs de los objetos]
431 * ----------------------------------------------------------------------------------------------------
432 **/
433function objetosTipoTablaDestino(tipoObjeto, tablaDestinoIdRef, objetosExcluir) {
434
435 // Se preparan las variables de trabajo
436 var objetosIdRef = [];
437 var proyectoPrincipal = theApp.mainProjectInfo();
438
439 // Leer todos los objetos del tipo
440 var numObjetos = proyectoPrincipal.allObjectCount(tipoObjeto);
441 for (var numObjeto = 0; numObjeto < numObjetos; numObjeto++) {
442 // Si el objeto no es privado y su tabla de destino se corresponde con la solicitada se añade al array a retornar
443 var objeto = proyectoPrincipal.allObjectInfo(tipoObjeto, numObjeto);
444 if ((objeto.isPrivate() == false) && (objeto.outputTable().idRef() == tablaDestinoIdRef)) {
445 // Se verifica que el objeto no esté en el array de objetos a excluir recibido
446 if (objetosExcluir.indexOf(objeto.idRef()) == -1) {
447 objetosIdRef.push(objeto.idRef());
448 }
449 }
450 }
451
452 // Retornamos el array con los idRefs de los objetos encontrados
453 return objetosIdRef;
454}
455
456
457/**
458 * ----------------------------------------------------------------------------------------------------
459 * Lista de campos de un tabla [Devuelve array con identificadores de campos no privados de un tabla]
460 *
461 * @param {[String]} tablaIdRef [Requerido idRef de la tabla]
462 * @param {[Array]} camposExcluir [Requerido Array de identificadores de los campos a excluir de la lista a retornar]
463 * @return [{String}] camposId [Array de identificadores de los campos]
464 * ----------------------------------------------------------------------------------------------------
465 **/
466function camposTabla(tablaIdRef, camposExcluir) {
467
468 // Se preparan las variables de trabajo
469 var camposId = [];
470
471 // Obtenemos el tableInfo de la tabla recibida
472 var proyectoInfo = theApp.projectInfo(tablaIdRef.split("/")[0]);
473 var tablaInfo = proyectoInfo.objectInfo(VObjectInfo.TypeTable, tablaIdRef.split("/")[1]);
474 if (tablaInfo) {
475 // Leemos los campos de la tabla
476 var numCampos = tablaInfo.subObjectCount(VObjectInfo.TypeField);
477 for (var numCampo = 0; numCampo < numCampos; numCampo++) {
478 // Si el campo no es privado se añade a lista de retorno
479 var campo = tablaInfo.subObjectInfo(VObjectInfo.TypeField, numCampo);
480 if (campo.isPrivate() == false) {
481 // Se verifica que el campo no esté en el array de campos a excluir recibido
482 if (camposExcluir.indexOf(campo.id()) == -1)
483 camposId.push(campo.id());
484 }
485 }
486 }
487
488 // Retornamos la lista de campos obtenidos
489 return camposId;
490}
491
492
493/**
494 * ----------------------------------------------------------------------------------------------------
495 * tablaObjeto [Devuelve un objeto con el tablaInfo del idRef recibido]
496 *
497 * @param {[tablaIdRef]} IdRef de la tabla [Requerido]
498 * @return {Object} objeto de la clase VTableInfo
499 * ----------------------------------------------------------------------------------------------------
500 **/
501function tablaObjeto(tablaIdRef) {
502
503 var proyectoAlias = tablaIdRef.split("/")[0];
504 var tablaId = tablaIdRef.split("/")[1];
505 var proyectoInfo = theApp.projectInfo(proyectoAlias);
506 var tablaInfo = proyectoInfo.tableInfo(tablaId);
507 return tablaInfo;
508}
509
510
511/**
512 * ----------------------------------------------------------------------------------------------------
513 * imagenToBase64 [Devuelve una cadena con el valor de una imagen en base64]
514 *
515 * @param {[VImage]} imagen [Requerido objeto de la clase VImage]
516 * @return {String} cadena que representa la imagen en base64
517 * ----------------------------------------------------------------------------------------------------
518 **/
519function imagenToBase64(imagen) {
520
521 var byteArray = new VByteArray();
522 byteArray = imagen.saveToData("PNG",0);
523 var imagenBase64 = byteArray.toBase64().toLatin1String();
524 return imagenBase64;
525}
526
527
528/**
529 * ----------------------------------------------------------------------------------------------------
530 * imagenFromBase64 [Devuelve una imagen a partir de una cadena en base64]
531 *
532 * @param {[imagenBase64]} cadena que contiene una imagen en formato base 64
533 * @return {VImage} objeto de la clase VImage
534 * ----------------------------------------------------------------------------------------------------
535 **/
536function imagenFromBase64(imagenBase64) {
537
538 // Convertimos el buffer en base64 en un byteArray
539 var ba = new VByteArray();
540 var ba64 = new VByteArray();
541 ba.setText(imagenBase64);
542 ba64.fromBase64(ba);
543
544 // Intentamos obtener la imagen en 4 formatos diferentes soportados
545 var imagen = new VImage();
546 imagenOk = imagen.loadFromData(ba64, "PNG");
547 if (imagenOk == false) { imagenOk = imagen.loadFromData(ba64, "JPG"); }
548 if (imagenOk == false) { imagenOk = imagen.loadFromData(ba64, "JPEG"); }
549 if (imagenOk == false) { imagenOk = imagen.loadFromData(ba64, "BMP"); }
550
551 // Retornamos la imagen obtenida
552 return imagen;
553}
554
555
556/**
557 * ----------------------------------------------------------------------------------------------------
558 * esValidoJson [Devuelve true si el JSON es válido]
559 *
560 * @param {json} Objeto JSON
561 * @return {Boolean} True si es válido el JSON y false si tiene errores
562 * ----------------------------------------------------------------------------------------------------
563 **/
564function esValidoJson(json) {
565
566 try {
567 JSON.parse(json);
568 return true;
569 } catch (e) {
570 return false;
571 }
572}