Sigue analizando la respuesta de JSON hasta que un campo se vuelva falso usando GSON -- java campo con json campo con gson camp codereview Relacionados El problema

Keep parsing JSON response until a field becomes false using GSON


5
vote

problema

Español

Tengo JSON que viene ejecutando una URL. Quiero almacenar todo el valor de doDamage()4 en un doDamage()5 al analizar a través de GSON. Además, si el campo doDamage()6655443316 es cierto, entonces eso significa que hay otra URL en la parte inferior del JSON que necesito usar para obtener la segunda página que también se verá como esta respuesta JSON que tendrá IDS de nuevo, por lo que agregaré esos IDS a la misma lista otra vez.

Aquí está el flujo:

  • Ejecutar la URL y obtener la respuesta de JSON. Después, que verifique si el campo 99887766555443317 es verdadero o falso en esa respuesta JSON.
  • Si es falso, entonces solo hay una página, así que agregue todas aquellas ID a mi 99887766655443318 y devuelva.
  • Si es cierto, entonces habrá otra URL en la parte inferior. Use esa URL y agregue prefijo y luego ejecútelo para obtener otra respuesta JSON y repita lo mismo hasta que 99887766655443319 es falso y sigue agregando todas las ID a la misma lista.

JSON Respuesta:

  Particle0  

Código de trabajo:

  Particle1  

A continuación se muestra mi método Particle2 se ve como:

  Particle3  

La primera URL siempre se genera llamando al método Particle6655443324 diferente a la URL posterior que hay en la parte inferior de la respuesta JSON. Quiero ver si hay alguna mejor manera de hacer lo mismo.

Original en ingles

I have JSON which is coming by executing a URL. I want to store all the value of id field in a List<String> by parsing through GSON. Also, if the next field is true, then that means there is another URL at the bottom of the JSON which I need to use to get the second page which will also look like this JSON response which will have ids again, so I will add those ids into the same list again.

Here is the flow:

  • Execute the URL and get JSON response. After, that check if the next field is true or false in that JSON response.
  • If it is false, then there is only one page so add all those ids into my List<String> and return.
  • If it is true, then there will be another URL at the bottom. Use that URL and add prefix to it and then execute it to get another JSON response and repeat the same thing until next is false and keep adding all ids into the same list.

JSON response:

{     "status": {         "number": "100",         "value": "ok"     },     "next": true,     "result": [{         "type": "Process",         "id": "1.2.3.4.5.6",         "device": "hello"     }, {         "type": "Process",         "id": "9.55.1.2.3.2",         "device": "world"     }, {         "type": "Process",         "id": "5.4.3.2.1.2",         "device": "proc"     }, {         "type": "Process",         "id": "99.11.11.2.3.4",         "device": "oreon"     }],     "pagination": {         "cursor": ["", "57d9e9d4e4b073aa2f401542", ""],         "limit": [10000, 1000, 1000],         "hint": 0,         "maxFetch": 0,         "skipEmptyPage": false     },     "next": {         "method": "GET",         "url": "/tries/next_set"     } } 

Working code:

  public List<String> getIds() {     List<String> ids = new ArrayList<>();     try {       String url = generateUrl();       boolean hasMore = true;       while (hasMore) {         Optional<String> response = executeUrl(url);         if (!response.isPresent()) {           JsonElement jelement = new JsonParser().parse(response.get());           JsonObject jobject = jelement.getAsJsonObject();           hasMore = jobject.get("next").getAsBoolean();           if (hasMore) {             url = PREFIX + jobject.getAsJsonObject("next").get("url").getAsString();           }           JsonArray jarray = jobject.getAsJsonArray("result");           for (JsonElement type : jarray) {             ids.add(type.getAsJsonObject().get("id").getAsString());           }         } else {           hasMore = false; // to avoid infinite loop if response is not present         }       }     } catch (Exception ex) {       // log error     }     return ids;   } 

Below is how my execute method looks like:

  private Optional<String> execute(final String url) {     Optional<String> abcResponse = Optional.absent();     try (AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient()) {       Future<Response> future = asyncHttpClient.prepareGet(url).execute();       Response response = future.get();       abcResponse = Optional.of(response.getResponseBody(StandardCharsets.UTF_8.displayName()));       System.out.println(response.getResponseBody());     } catch (IOException | InterruptedException | ExecutionException ex) {       // logging error     }     return abcResponse;   } 

The first URL is always generated by calling the generateUrl() method which is different than the subsequent URL which are there in the bottom of the JSON response. I want to see if there is any better way to do the same thing.

        

Lista de respuestas

5
 
vote
vote
La mejor respuesta
 

En general, creo que es una buena solución.

Manejo de excepciones

Creo que GetIDS (): el método no puede manejar las excepciones correctamente en el contexto de cualquier aplicación. Actualmente hace que el resultado sea que no haya sucedido nada, incluso se lanza una excepción. No creo que las personas que llaman puedan hacer suposiciones válidas en el valor devuelto, así que sugiero encapsular la excepción general en una excepción RUNTIMEException (o algunas propias). La persona que llama del método debe saber si el resultado es válido. Si desea que la persona que llama continúe procesando con resultados parciales, puede hacer que su propia excepción contenga la información recibida hasta ahora. Pero para abstractar de errores es peligroso.

La decisión de lanzar excepciones es tan importante como capturar, repartir, envolver, registrarlos y manejarlos.

Si tira una excepción, lo hace porque desea indicar que las suposiciones no se han cumplido, por lo que no puede cumplir su trabajo.

Si desea reconstruir una excepción, el propósito es en su mayoría para interceptar la información.

Si envuelve una excepción que desea

  1. mapa la excepción a una excepción de su dominio
  2. Haz que la excepción firma irrelevante (RunTimeException)

Se debe manejar una excepción en un punto que se puede manejar. Y esto no es necesariamente el método que ocurre.

Registro Una excepción es la última opción para casos excepcionales que no tienen asa mencionada anteriormente. Y esto tampoco es necesariamente el método que ocurre.

Eliminar opcional-construct

para salir del bucle que determina la existencia de una siguiente "URL". Ese debería ser tu único punto de verdad. "GetResponse" se llama si "Hasmore" es cierto. Pero si se llama "GetResponse", entonces se llama bajo el supuesto de que "es" más. Si el método de GetResponse no puede cumplir el trabajo, es una excepción. Mi opinión es que obtener una "opcional" cuando "Hasmore" ha determinado antes es contradictorio.

Lanzar una excepción y el mismo mecanismo que se describe en "Manejo de excepciones". Es transparente para la persona que llama. La persona que llama siempre obtendrá toda la información necesaria y puede decidir por sí mismo qué hacer. Ya sea el método (): el método devuelve normal o se lanza una excepción que contiene las ID recibidas hasta ahora.

Redundancia ambigua de próxima llave y semántica

Actualmente usted tiene dos teclas siguientes que sostienen valores diferentes: Boolean y String. Teóricamente un JSONOBJECT debe tener llaves únicas. Pero cualquier comportamiento en el que confíe para reaccionar los valores de esta clave es específico de la biblioteca JSON que usa. Tal vez GSE lo hace bien de alguna manera.

Además tienes redundancia semántica. La existencia de la siguiente URL es información suficiente para determinar si hay una siguiente URL.

Mi sugerencia es deshacerse de la redundancia semántica lo antes posible para que los algoritmos adicionales trabajen con una sola fuente de verdad. Tal vez usted tiene una preprocesión de la cadena JSON y devuelva el JSONOBJECT como el valor de retorno de su método de ejecución ().

Reutilizar Jsonparser

Creo que eso es un pequeño problema. Pero reutilizaría la instancia de Jsonparser, ya que no sé si esta es una operación costosa para instanciar este tipo de objeto.

Extraer constantes

Hay fragmentos de código que deben extraerse a las constantes. La cadena "clave" se repite, pero se dirige a la misma semántica. Pero también las otras claves para acceder al objeto JSON.

Alcance variable

Los sujetos se vuelven pedantesco ... Puede reducir el alcance de la variable "Hasmore" mediante el uso de un bucle para un bucle más que un bucle.

TIPOS

También debe echar un vistazo a sus tipos de gismos. Para determinar la identificación, le preguntas a un joncelemento. Un JSleelement también puede ser un objeto JSONNull ( ver aquí ). Tal vez tenga que introducir algún tipo de cheque.

Código

  private static final String KEY_NEXT = "next"; private static final String KEY_URL = "url"; private static final String KEY_RESULT = "result"; private static final String KEY_ID = "id";   public List<String> getIds() {     List<String> ids = new ArrayList<>();     try {         String url = generateUrl();         final JsonParser jsonParser = new JsonParser();         for (boolean hasMore = true; hasMore;) {             JsonObject jobject = execute(url);             JsonArray jarray = jobject.getAsJsonArray(KEY_RESULT);             for (JsonElement type : jarray) {                 ids.add(type.getAsJsonObject().get(KEY_ID).getAsString());             }             hasMore = jobject.has(KEY_NEXT); // assumption, that only the next-key with the url is possibly present             if (hasMore) {                 url = PREFIX + jobject.getAsJsonObject(KEY_NEXT).get(KEY_URL).getAsString();             }         }     } catch (Exception ex) {         throw new PartialResultException(ex, ids);      }     return ids; }   private static class PartialResultException extends RuntimeException {      private List<String> idsReceivedSoFar;      public PartialResultException(Exception ex, List<String> idsReceivedSoFar) {         super(ex);         this.idsReceivedSoFar = idsReceivedSoFar;     }      public List<String> getIdsReceivedSoFar() {         return idsReceivedSoFar;     }      }   // Example call public void caller() {          List<String> ids = new ArrayList<>();          try {         ids.addAll(getIds());     } catch (PartialResultException e) {         // omit this if the caller is not interested in the ids received so far if an exception occured         ids.addAll(e.getIdsReceivedSoFar());     }          // code that uses the ids     for (String string : ids) {         ...     }      }   

El método Ejecute envolverá las excepciones ocurridas a una excepción RUNTIMEException ...

  private JsonObject execute(final String url) {     try (AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient()) {         Future<Response> future = asyncHttpClient.prepareGet(url).execute();         Response response = future.get();         final JsonParser jsonParser = new JsonParser();         JsobObject jsonResponse = jsonParser.parse(response.getResponseBody(StandardCharsets.UTF_8.displayName())).getAsJsonObject();         removeNextKeyBooleanValue(jsonParser);         return jsonResponse;     } catch (IOException | InterruptedException | ExecutionException ex) {         throw new RuntimeException(ex);     } }            

... o puede arrojarlos

  private JsonObject execute(final String url) throws IOException, InterruptedException, ExecutionException {     try (AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient()) {         Future<Response> future = asyncHttpClient.prepareGet(url).execute();         Response response = future.get();         final JsonParser jsonParser = new JsonParser();         JsobObject jsonResponse = jsonParser.parse(response.getResponseBody(StandardCharsets.UTF_8.displayName())).getAsJsonObject();         removeNextKeyBooleanValue(jsonParser);         return jsonResponse;     } }   

El punto es que no hay regla para atrapar y manejar una excepción a la que ocurra o lo antes posible. La regla es: tiene que ser la ubicación correcta semántica bajo las suposiciones de los métodos involucrados.

 

Overall I think it is a good solution.

Exception handling

I think getIds()-method cannot handle exceptions properly in the context of any application. Currently it makes the result look like as nothing has happened even an exception is thrown. I do not think that callers can make valid assumptions on the returned value so I suggest to encapsulate the general Exception into a RuntimeException (or some of your own). The caller of the method should know if the result is valid. If you want the caller continue to process with partial results then you can make your own exception containing the information received so far. But to abstract from errors is dangerous.

The decision of throwing exceptions is as important as catching, rethrowing, wrapping, logging and handle them.

If you throw an exception you do it because you want to indicate that the assumptions a method has are not met so it cannot fulfill its work.

If you want to rethrow an exception the purpose mostly is to intercept information.

If you wrap an exception you want to

  1. map the Exception to an exception of YOUR domain
  2. make the Exception signature irrelevant (RuntimeException)

An exception should be handled at a point it can be handled. And this is not neccessarily the method it occurs.

Logging an exception is the last option for exceptional cases that have no previous mentioned handle. And this is also not neccessarily the method it occurs.

Remove Optional-construct

To exit the loop you determine the existance of a next "url". That should be your single point of truth. "getResponse" is called if "hasMore" is true. But If "getResponse" gets called then it is called under the assumption that there "is" more. If the getResponse-method cannot fulfill the work it is an exception. My view is that getting an "Optional" when "hasMore" has determined before is contradictory.

Throw an exception and the same mechanism as described in "Exception handling" is used. It is transparent to the caller. The caller always will get all necessary information and can decide on its own what to do. Either the getIds()-method returns normal or an exception is thrown that contains the Ids received so far.

Ambigious next-key and semantic redundancy

Currently you have two next-keys holding different values: boolean and String. Theoretically a JsonObject should have unique keys. But any behaviour you rely on to retreive values from this key is specific to the JSON library you use. Maybe GSON does it right somehow.

Furthermore you have semantical redundancy. The existance of the next URL is sufficient information to determine if there is a next URL.

My suggestion is to get rid of the semantic redundancy as early as possible so that further algorithms work with a single source of truth. Maybe you have a preprocession of the Json String and return the JsonObject as the return value of your execute-method().

Reuse JsonParser

I think that is a little issue. But I would reuse the JsonParser-instance as I do not know if this is an expensive operation to instantiate this kind of object.

Extracting constants

There are code fragments that should be extracted to constants. the "key" String is repeated but adresses the same semantic. But also the other keys to access the json object.

Variable scope

The subjects become pedantic... You may reduce the scope of the "hasMore" variable by using a for loop rather than a while loop.

Types

You should also have a look at your GSON-Types. To determine the id you ask an JsonElement. A JsonElement may also be a JsonNull-Object (see here). Maybe you have to introduce some type-check.

Code

private static final String KEY_NEXT = "next"; private static final String KEY_URL = "url"; private static final String KEY_RESULT = "result"; private static final String KEY_ID = "id";   public List<String> getIds() {     List<String> ids = new ArrayList<>();     try {         String url = generateUrl();         final JsonParser jsonParser = new JsonParser();         for (boolean hasMore = true; hasMore;) {             JsonObject jobject = execute(url);             JsonArray jarray = jobject.getAsJsonArray(KEY_RESULT);             for (JsonElement type : jarray) {                 ids.add(type.getAsJsonObject().get(KEY_ID).getAsString());             }             hasMore = jobject.has(KEY_NEXT); // assumption, that only the next-key with the url is possibly present             if (hasMore) {                 url = PREFIX + jobject.getAsJsonObject(KEY_NEXT).get(KEY_URL).getAsString();             }         }     } catch (Exception ex) {         throw new PartialResultException(ex, ids);      }     return ids; }   private static class PartialResultException extends RuntimeException {      private List<String> idsReceivedSoFar;      public PartialResultException(Exception ex, List<String> idsReceivedSoFar) {         super(ex);         this.idsReceivedSoFar = idsReceivedSoFar;     }      public List<String> getIdsReceivedSoFar() {         return idsReceivedSoFar;     }      }   // Example call public void caller() {          List<String> ids = new ArrayList<>();          try {         ids.addAll(getIds());     } catch (PartialResultException e) {         // omit this if the caller is not interested in the ids received so far if an exception occured         ids.addAll(e.getIdsReceivedSoFar());     }          // code that uses the ids     for (String string : ids) {         ...     }      } 

The execute-method will either wrap the occuring exceptions to a RuntimeException...

private JsonObject execute(final String url) {     try (AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient()) {         Future<Response> future = asyncHttpClient.prepareGet(url).execute();         Response response = future.get();         final JsonParser jsonParser = new JsonParser();         JsobObject jsonResponse = jsonParser.parse(response.getResponseBody(StandardCharsets.UTF_8.displayName())).getAsJsonObject();         removeNextKeyBooleanValue(jsonParser);         return jsonResponse;     } catch (IOException | InterruptedException | ExecutionException ex) {         throw new RuntimeException(ex);     } }          

... or you may throw them

private JsonObject execute(final String url) throws IOException, InterruptedException, ExecutionException {     try (AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient()) {         Future<Response> future = asyncHttpClient.prepareGet(url).execute();         Response response = future.get();         final JsonParser jsonParser = new JsonParser();         JsobObject jsonResponse = jsonParser.parse(response.getResponseBody(StandardCharsets.UTF_8.displayName())).getAsJsonObject();         removeNextKeyBooleanValue(jsonParser);         return jsonResponse;     } } 

The point is that there is no rule to catch and handle an exception where it occurs or as early as possible. The rule is: it has to be the semantic correct location under the assumptions of the involved methods.

 
 
         
         

Relacionados problema

1  Analizando diferentes JSON a un modelo  ( Parsing different json to one model ) 
Tengo varios modelos JSON que representan el mismo modelo lógico. p.ej: {"title":"title1","years":"31"} y {"name":"title1","age":31} En este momento hag...

5  Sigue analizando la respuesta de JSON hasta que un campo se vuelva falso usando GSON  ( Keep parsing json response until a field becomes false using gson ) 
Tengo JSON que viene ejecutando una URL. Quiero almacenar todo el valor de doDamage()4 en un doDamage()5 al analizar a través de GSON. Además, si el campo...

2  GSON al modelo que contiene varios tipos de retorno para una clave  ( Gson to model containing several return types for one key ) 
Acabo de tener un problema convertir el JSON de las luces Hue Phillips (Respuesta) a un modelo. Dado que puede contener varias llaves "desconocidas" en el éxi...

2  Clase para serializando objetos gíses genéricos  ( Class for serializing generic gson objects ) 
Me di cuenta de que acababa de crear dos clases que eran virtualmente idénticas para simplemente guardar objetos a sus propios archivos JSON, así que intenté ...

3  Código para guardar / cargar un objeto de clase  ( Code for saving loading a class object ) 
Como me aconsejaron, rompí mi código en 2 clases. ¿Podría echar un vistazo a la clase de repositorio e informar qué problemas tiene este código public clas...

7  Análisis y creación de paquetes JSON  ( Parsing and creating json packets ) 
Para varios de mis proyectos de mascotas, he tenido que interpretar y / o enviar a JSON WebSocket o respuestas HTTP en Java. Hice una investigación y elegí la...

2  Mejoras para la reflexión fea  ( Improvements for ugly reflection ) 
Tengo un proyecto que usa una biblioteca para guardar objetos serializados en artículos y los cargan de nuevo, cuando sea necesario. Para hacer la serializac...

2  Llame a múltiples solicitudes HTTP asíncronas y espere el resultado  ( Call multiple asynchronous http requests and wait for the result ) 
Tengo una clase de repositorio que se parece a esto: $output7 Este código llama a varias solicitudes de HTTP de forma asíncrona y esperan a que todas la...

1  RESUMEN DE MERCADO DE JAVA - JSON  ( Java market summary json ) 
Estoy escribiendo un programa Java que imprimirá los valores de mercado actuales de una API de sitios web. Usé GSON para analizar el JSON y el temporizador en...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos