Lectura de un documento JSON conociendo y sin conocer que propiedades contiene

Escrito por picodotdev el .
java planeta-codigo programacion
Enlace permanente Comentarios

Procesar un JSON cuya estructura no se conoce de antemano es un problema habitual cuando parte de sus propiedades son dinámicos y no se pueden mapear a un objeto en tiempo de compilación. En este artículo veremos cómo descubrir y recorrer esos atributos desconocidos con Jackson, utilizando JsonNode para inspeccionar el tipo y el valor de cada campo. Además, comprobaremos cómo lograr el mismo resultado con la API estándar de Java, para que puedas elegir la opción que mejor encaje en tu proyecto.

Java

Habitualmente al procesar datos en formato JSON se conoce la estructura de datos origen y es posible mapear esos datos a un objeto. En ocasiones el JSON puede tener una parte dinámica como una colleción de atrubitos que se conoce que existirá pero no se conoce que nombres tienen los atributos que se incluyen. Al tener una parte diámica no es posible mapaear completamente los datos a un objeto ya que se desconocen las propiedades y los tipos de esas propiedades.

Tanto la librería Jackson como la API proporcionada por Java ofrecen la posiblidad de descubrir cuales son las propiedades del JSON.

Leer un JSON de forma dinámica con Jackson

Jackson ofrecen un una clase que representa el nodo raiz de on JSON, con una instancia de este objeto es posible conocer el tipo de nodo, de que propiedades se compone el JSON y los tipos de datos de esas propiedades.

El siguiente ejemplo imprime en la consola algunas de las propiedades en el objeto raiz. Estas propiedades y valores son desonoccidos en tiempo de compilación. En el ejemplo se muestara también el uso de JSON-P para alguno de las propiedades que sean conocidos en tiempo de compilación.

El archivo de datos y el programa de ejemplo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "id": 1,
  "city": "Madrid",
  "population": 3543630,
  "other": {
    "country": "Spain",
    "area": 604.3,
    "timezone": "CET"
  }
}
data.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package io.github.picodotdev.blogbitix.javajsonread;

public class Main {

    public static void main(String[] args) {
        {
            System.out.println("Jackson");
            ObjectMapper mapper = new ObjectMapper();
            JsonNode root = mapper.readTree(Main.class.getResourceAsStream("/data.json"));

            String city1 = root.get("city").asString();
            String city2 = root.at("/city").asString();
            System.out.println("City 1: " + city1);
            System.out.println("City 2: " + city2);

            root.propertyStream().filter(p -> p.getValue().isValueNode()).forEach(p -> System.out.println(p.getKey() + " = " + p.getValue().asString()));

            System.out.println();

            root.at("/other").properties().forEach(p -> System.out.println(p.getKey() + " = " + p.getValue().asString()));
        }

        ...
    }
}
Main-1.java

La salida en la consola del programa es el siguiente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Jackson
City 1: Madrid
City 2: Madrid
id = 1
city = Madrid
population = 3543630

country = Spain
area = 604.3
timezone = CET
System-1.out

Leer unn JSON a un Map

Otra forma de leer el JSON es mapeandolo a un objeto Map.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package io.github.picodotdev.blogbitix.javajsonread;

public class Main {

    public static void main(String[] args) {
        ...
    
        {
            System.out.println("Jackson (map)");
            ObjectMapper mapper = new ObjectMapper();
            InputStream stream = Main.class.getResourceAsStream("/data.json");
            Map<String, Object> map = mapper.readValue(stream, new TypeReference<>() {});

            System.out.println(map);

            System.out.println();

            System.out.println("City 1: " + map.get("city"));

            map.entrySet().stream().filter(e -> !(e.getValue() instanceof Map)).forEach(e -> System.out.println(e.getKey() + " = " + e.getValue()));

            System.out.println();

            ((Map<String, Object>) map.get("other")).forEach((key, value) -> System.out.println(key + " = " + value));
        }

        ...
    }
}
Main-2.java

La salida en la consola del programa es el siguiente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Jackson (map)
{id=1, city=Madrid, population=3543630, other={country=Spain, area=604.3, timezone=CET}}

City 1: Madrid
id = 1
city = Madrid
population = 3543630

country = Spain
area = 604.3
timezone = CET
System-2.out

Leer un JSON de forma dinámica con la API de Java

El siguiente ejemplo es el equivalente que on Jackson pero utilizando APIs ofrecidas por Java.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package io.github.picodotdev.blogbitix.javajsonread;

...

public class Main {

    public static void main(String[] args) {
        ...

        {
            System.out.println("Jakarta JSON");
            JsonReader reader = Json.createReader(Main.class.getResourceAsStream("/data.json"));
            JsonStructure root = reader.read();

            JsonValue city1 = root.asJsonObject().get("city");
            JsonValue city2 = Json.createPointer("/city").getValue(root);
            System.out.println("City: " + city1);
            System.out.println("City: " + city2);

            root.asJsonObject().entrySet().stream()
                .filter(e -> List.of(ValueType.NULL, ValueType.STRING, ValueType.NUMBER, ValueType.TRUE, ValueType.FALSE).contains(e.getValue().getValueType()))
                .forEach(e -> System.out.println(e.getKey() + " = " + e.getValue().toString()));

            System.out.println();

            Json.createPointer("/other").getValue(root).asJsonObject().forEach((key, value) -> System.out.println(key + " = " + value.toString()));
        }
    }
}
Main-3.java

La salida en la consola del programa es el siguiente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Jakarta JSON
City: "Madrid"
City: "Madrid"
id = 1
city = "Madrid"
population = 3543630

country = "Spain"
area = 604.3
timezone = "CET"
System-3.out

Las librerías dependencias del ejemplo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[versions]
jackson = "3.0.0"
jakarta-json-api = "2.1.3"

[libraries]
jacksonDependencies = { module = "tools.jackson:jackson-bom", version.ref = "jackson" }

jackson-core = { module = "tools.jackson.core:jackson-core", version.ref = "jackson" }
jackson-databind = { module = "tools.jackson.core:jackson-databind", version.ref = "jackson" }

jakarta-json-api = { module = "jakarta.json:jakarta.json-api", version.ref = "jakarta-json-api" }
parsson = { module = "org.eclipse.parsson:parsson", version = "1.1.9" }

[bundles]
jackson = [
  "jackson-core",
  "jackson-databind"
]

jakarta = [
  "jakarta-json-api",
  "parsson"
]
libs.versions.toml
Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run


Comparte el artículo: