2
respuestas

[Duda] Duda Proyecto ScreenMatch Error mapear totalDelTemporadas N/A desde

Mi duda es como puedo solucionar un java.lang.RuntimeException: generado al mapear los datos con Jackson: com.fasterxml.jackson.databind.exc.InvalidFormatException. Debido a que no puede serializar el tipo String "N/A" a interger: Cannot deserialize value of type java.lang.Integer from String "N/A": not a valid java.lang.Integer value. Ese error pasa con las series que no cuentan con un numero de temporadas dentro de los datos del API.

at [Source: (String)"{"Title":"Matrix","Year":"1993","Rated":"N/A","Released":"01 Mar 1993","Runtime":"60 min","Genre":"Action, Drama, Fantasy","Director":"N/A","Writer":"Grenville Case","Actors":"Nick Mancuso, Phillip Jarrett, Carrie-Anne Moss","Plot":"Steven Matrix is one of the underworld's foremost hitmen until his luck runs out, and someone puts a contract out on him. Shot in the forehead by a .22 pistol, Matrix \"dies\" and finds himself in \"The City In Between\", where he is ...","Language":"English","Countr"[truncated 368 chars]; line: 1, column: 845] (through reference chain: com.aluracursos.screenmatch.model.DatosSerie["totalDeTemporadas"])

Ejemplo con la serie Matrix.Ejemplo de resultados del API para serie Matrix en postman Ejemplo de resultados del API para serie Matrix en postman ]

Que archivos debería modificar, que código sostiene las mejores practicas para solventar ese error. Log del Error:

Por favor escribe el nombre de la serie que deseas buscar
matriz
2024-05-11T23:52:46.797-05:00  INFO 20416 --- [screenmatch] [           main] .s.b.a.l.ConditionEvaluationReportLogger : 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-05-11T23:52:46.829-05:00 ERROR 20416 --- [screenmatch] [           main] o.s.boot.SpringApplication               : Application run failed

java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.lang.Integer` from String "N/A": not a valid `java.lang.Integer` value
 at [Source: (String)"{"Title":"Matriz","Year":"2023–","Rated":"N/A","Released":"N/A","Runtime":"N/A","Genre":"Drama","Director":"N/A","Writer":"N/A","Actors":"Paula Foncea, Vicente Vergara, Farah Hamed","Plot":"N/A","Language":"Spanish","Country":"Spain","Awards":"1 win & 17 nominations","Poster":"https://m.media-amazon.com/images/M/MV5BMTFiYjgzNzUtZTJmZC00NzVjLWExMjMtYjQ1YTE1ZDY2MTgzXkEyXkFqcGdeQXVyMTEwNTc0Mzkw._V1_SX300.jpg","Ratings":[],"Metascore":"N/A","imdbRating":"N/A","imdbVotes":"N/A","imdbID":"tt14404586","[truncated 55 chars]; line: 1, column: 532] (through reference chain: com.aluracursos.screenmatch.model.DatosSerie["totalDeTemporadas"])
    at com.aluracursos.screenmatch.service.ConvierteDatos.obtenerDatos(ConvierteDatos.java:14) ~[classes/:na]
    at com.aluracursos.screenmatch.principal.Principal.muestraElMenu(Principal.java:28) ~[classes/:na]
    at com.aluracursos.screenmatch.ScreenmatchApplication.run(ScreenmatchApplication.java:19) ~[classes/:na]
    at org.springframework.boot.SpringApplication.lambda$callRunner$5(SpringApplication.java:790) ~[spring-boot-3.2.5.jar:3.2.5]
    at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.1.6.jar:6.1.6]
    at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.1.6.jar:6.1.6]
    at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[spring-core-6.1.6.jar:6.1.6]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-3.2.5.jar:3.2.5]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:789) ~[spring-boot-3.2.5.jar:3.2.5]
    at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:774) ~[spring-boot-3.2.5.jar:3.2.5]
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) ~[na:na]
    at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[na:na]
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774) ~[spring-boot-3.2.5.jar:3.2.5]
    
    
2 respuestas

Según encontré solo tengo tres opciones cambiar el campo a String y luego tratar ese campo o atributo para tratar la excepción, crear una clase personalizada para manejar esa excepción o utilizar el método estático de fábrica en lugar de un constructor público para personalizar el proceso de deserialización al nivel del constructor o método de principal. por ejemplo: para crear una clase personalizada para manejar esa excepción:

public class CustomIntegerDeserializer extends JsonDeserializer<Integer> {
    @Override
    public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String value = p.getText();
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return null; // o cualquier otro valor predeterminado
        }
    }
}

Y luego usarla dentro del Record de la siguiente forma:

@JsonIgnoreProperties(ignoreUnknown = true)
public record DatosSerie(
        @JsonAlias("Title") String titulo,
        @JsonDeserialize(using = CustomIntegerDeserializer.class)
        @JsonAlias("totalSeasons") Integer totalTemporadas,
        @JsonAlias("imdbRating") String evaluacion,
        @JsonAlias("Poster") String poster,
        @JsonAlias("Genre") String genero,
        @JsonAlias("Actors") String actores,
        @JsonAlias("Plot") String sinopsis) {
}

Pero no se, si sea la más eficiente o siga las buenas practicas de programación. O puedo utilizar el método estático de fábrica en lugar de un constructor público para personalizar el proceso de deserialización al nivel del constructor o método de principal.

@JsonIgnoreProperties(ignoreUnknown = true)
public record DatosSerie(
        @JsonAlias("Title") String titulo,
        @JsonAlias("imdbRating") String evaluacion,
        @JsonAlias("Poster") String poster,
        @JsonAlias("Genre") String genero,
        @JsonAlias("Actors") String actores,
        @JsonAlias("Plot") String sinopsis,
        Integer totalTemporadas) {

    @JsonCreator
    public static DatosSerie of(
            @JsonProperty("Title") String titulo,
            @JsonProperty("imdbRating") String evaluacion,
            @JsonProperty("Poster") String poster,
            @JsonProperty("Genre") String genero,
            @JsonProperty("Actors") String actores,
            @JsonProperty("Plot") String sinopsis,
            @JsonProperty("totalSeasons") String totalTemporadas) {
        
        Integer totalTemporadasInt;
        try {
            totalTemporadasInt = Integer.parseInt(totalTemporadas);
        } catch (NumberFormatException e) {
            totalTemporadasInt = -1; // o cualquier otro valor predeterminado
        }

        return new DatosSerie(titulo, evaluacion, poster, genero, actores, sinopsis, totalTemporadasInt);
    }
}

Cuales son las ventajas y desventajas de las tres opciones, cual es la solución más optima, y cual es la mejor opción siguiendo las buenas practicas y porque, de antemano, gracias. y que valor por defecto debo colocar para las series que no tienen numero de temporadas, yo coloque 1, porque si están al menos deben tener una temporada. En mi caso emplee la opción 3, me parece más eficiente y manejo la excepción directamente de donde se genera, pero quiero saber que otras formas o soluciones existen solucionar esa excepción y espero que a alguien más le sirva esta solución,

¡Hola Jonathan, espero que estés bien!

Gracias por compartir tu problema y solución con nosotros :)

Entiendo que estás experimentando un error al tratar de mapear los datos con Jackson debido a que no puede serializar el tipo String "N/A" a un entero. Este error ocurre con las series que no cuentan con un número de temporadas dentro de los datos del API.

Para solucionar este problema, puedes considerar modificar el código para manejar el valor "N/A" de manera especial, por ejemplo, asignándole un valor predeterminado cuando se encuentre este caso. Puedes utilizar la anotación @JsonDeserialize de Jackson para personalizar la deserialización de los datos. Aquí tienes un ejemplo de cómo podrías hacerlo:

public class DatosSerie {
    // otros atributos
    
    @JsonDeserialize(using = CustomIntegerDeserializer.class)
    private Integer totalDeTemporadas;
    
    // otros métodos
}

public class CustomIntegerDeserializer extends JsonDeserializer<Integer> {
    @Override
    public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String value = p.getValueAsString();
        if ("N/A".equals(value)) {
            return null; // o cualquier otro valor predeterminado que desees
        } else {
            return Integer.valueOf(value);
        }
    }
}

En este ejemplo, estamos creando un deserializador personalizado para el campo totalDeTemporadas que maneja el caso de "N/A" y lo convierte en un entero o en otro valor predeterminado según tus necesidades.

Espero que esta sugerencia te ayude a solucionar el problema. Recuerda que es importante ajustar el código según las necesidades específicas de tu aplicación.

¡Espero haber ayudado y buenos estudios!