Ilustración conceptual de un proyecto con dos niveles y grupos de elementos que representan la jerarquía de datos JSON.

JSON: representar datos anidados y entender lo que devuelve una API

Que una respuesta sea JSON válido no significa que contenga el dato que necesitas. Puede estar perfectamente escrita y no incluir el código de una puerta, devolverlo como null o colocarlo dentro de una colección que tu programa no está recorriendo. Dar por resuelta la integración porque el visor muestra unas llaves bien sangradas es una celebración bastante prematura. La tipografía, por impecable que resulte, todavía no ha aprendido a validar entregas.

JSON permite representar datos estructurados mediante objetos, arrays y valores. Vamos a leer un proyecto con niveles y elementos anidados, localizar cada campo en su contexto y distinguir tres estados que suelen acabar mezclados en un informe: ausente, nulo y vacío. Usaremos el mismo archivo didáctico en Python y C#. No es una respuesta de una API Autodesk; es una estructura pequeña preparada para aprender a recorrer datos antes de depender de un servicio real.

Tabla de contenidos

Las llaves agrupan propiedades; los corchetes reúnen elementos

Un objeto JSON contiene propiedades con nombre: una clave de texto y su valor. Un array contiene una secuencia de valores. Esa diferencia cambia cómo accedes al contenido. Si niveles es un array, necesitas recorrer sus elementos o seleccionar uno según un criterio; no puedes tratar toda la colección como si fuera el objeto de un nivel concreto. Las llaves y los corchetes no son adornos que coloca el editor para que el fichero parezca más informático.

En nuestro ejemplo, la raíz contiene una propiedad proyecto, cuyo valor es otro objeto. Dentro de él están el nombre, un indicador booleano y el array niveles. Cada nivel contiene su propio array elementos. Para llegar al código de una puerta debes conservar ese recorrido: proyecto, nivel, elemento y propiedad. Buscar la primera aparición de la palabra codigo en el texto completo no establece a qué puerta pertenece.

Guarda el contenido siguiente como proyecto.json, en UTF-8, dentro de una carpeta vacía. Los identificadores 001, 002 y 003 están entre comillas porque son texto. 2.1 es un número, false es un booleano y null es un valor nulo. P1 tiene un array de elementos vacío: eso indica que esa colección existe y no contiene entradas en este documento.

				
					{
  "proyecto": {
    "nombre": "Centro cívico",
    "activo": false,
    "niveles": [
      {
        "id": "PB",
        "elementos": [
          {"id": "001", "altura_m": 2.1, "codigo": null},
          {"id": "002", "altura_m": 2.2},
          {"id": "003", "altura_m": 2.0, "codigo": ""}
        ]
      },
      {"id": "P1", "elementos": []}
    ]
  }
}
				
			

La sangría ayuda a leer, pero no convierte la jerarquía en una propiedad visual. La misma estructura puede representarse en una sola línea. Tampoco deberías depender del orden de las propiedades de un objeto para decidir qué significa cada valor. En cambio, un array sí tiene orden; aun así, ese orden no es la identidad de un elemento BIM. Conserva los identificadores cuando necesites relacionar resultados.

Los tipos del formato no sustituyen el significado del campo

JSON admite objetos, arrays, texto, números, booleanos y nulo. Las claves utilizan comillas dobles; los valores de texto también. En el formato, los booleanos se escriben true y false, y el nulo null. Un comentario o una coma final no forman parte del JSON estándar de este ejercicio. Si el editor los tolera, comprueba qué admite el lector que realmente utilizará tu aplicación.

Un identificador como "001" sigue siendo texto aunque solo contenga dígitos. Un número JSON no lleva ceros iniciales de esa forma, y convertirlo en entero eliminaría la representación que necesitamos conservar. Una fecha, por su parte, suele viajar como texto con una convención acordada; JSON no incorpora un tipo universal de fecha que resuelva por sí solo zona horaria y calendario. El formato lleva el valor; el contrato de la API explica qué significa.

Lo mismo ocurre con activo: false. El dato existe y su valor es falso. Si lo interpretas como «no hay información» por una comprobación genérica de verdadero o falso, has descartado precisamente la respuesta que te habían enviado. Es una manera bastante eficiente de perder información sin que se mueva una sola coma del archivo.

La especificación de JSON en RFC 8259 describe el formato. No define que altura_m sea una altura de puerta, si admite valores negativos o cuándo un proyecto debe considerarse activo. Esas reglas necesitan documentación y validación propias. En este ejercicio mantendremos el nombre de unidad visible y nos concentraremos en leer la estructura, sin presentar una comprobación sintáctica como una validación BIM.

En Python recorre los niveles y pregunta por el campo correcto

El módulo json de Python convierte los objetos del documento en diccionarios y los arrays en listas. El nulo se representa como None y los booleanos como True o False dentro del programa. Guarda el siguiente código como leer_json.py, junto al archivo de datos. json.load recibe el archivo abierto; no estamos ejecutando el texto como instrucciones.

				
					import json

with open("proyecto.json", encoding="utf-8") as entrada:
    datos = json.load(entrada)

niveles = datos["proyecto"]["niveles"]
if not isinstance(niveles, list):
    raise TypeError("niveles debe ser un array")
for nivel in niveles:
    elementos = nivel["elementos"]
    if not isinstance(elementos, list):
        raise TypeError("elementos debe ser un array")
    for elemento in elementos:
        if "codigo" not in elemento:
            estado = "ausente"
        elif elemento["codigo"] is None:
            estado = "nulo"
        elif isinstance(elemento["codigo"], str):
            estado = "vacio" if elemento["codigo"] == "" else "valor"
        else:
            raise TypeError("codigo debe ser texto o null")
        print(f"{nivel['id']} | {elemento['id']} | {estado}")
				
			

Ejecuta la orden siguiente desde esa carpeta. Obtendrás tres líneas: PB | 001 | nulo, PB | 002 | ausente y PB | 003 | vacio. No aparece una cuarta línea para P1 porque su colección está vacía. Esto permite distinguir que un nivel no tenga elementos de que exista una puerta sin la propiedad que queremos revisar.

				
					python leer_json.py
				
			

La primera comprobación pregunta si existe la clave. Solo después examinamos su valor. Usar directamente elemento.get("codigo") sin comprobar presencia produciría None tanto para el campo ausente como para el nulo. Puede ser adecuado si has decidido tratarlos igual; en este informe borraría una diferencia que hemos pedido conservar. La comodidad del acceso no debería decidir el significado del dato a tus espaldas.

Los campos estructurales del ejemplo son obligatorios: proyecto, niveles, elementos e identificadores. Accedemos a ellos directamente y comprobamos que las dos colecciones sean listas; un objeto vacío no debe pasar por una colección vacía por casualidad. No hemos construido una validación completa de su esquema. La documentación de json de Python 3.12 explica la conversión; decidir qué propiedades son obligatorias corresponde al contrato que implementas, no al descodificador.

En C# inspecciona el tipo antes de convertir el valor

Para la lectura equivalente utilizaremos JsonDocument, de System.Text.Json. Permite recorrer el documento sin definir todavía clases para cada nivel y elemento. Es una opción útil para inspeccionar una estructura pequeña; una aplicación con un contrato estable puede preferir tipos propios y deserialización hacia ellos. No necesitamos resolver toda la arquitectura de una integración para averiguar qué contiene una respuesta.

Con el SDK de .NET 8 instalado, crea el proyecto con esta orden desde la carpeta del ejercicio. Conserva proyecto.json en esa carpeta y sustituye el Program.cs generado dentro de LectorJson por el bloque siguiente.

				
					dotnet new console -n LectorJson -f net8.0
				
			

GetProperty accede a las propiedades que nuestro ejemplo exige. TryGetProperty permite preguntar por la opcional sin confundir su ausencia con un valor nulo. ValueKind indica qué tipo de valor JSON hemos encontrado; comprobamos que sea texto antes de llamar a GetString. Una clave llamada codigo no garantiza por sí sola que alguien haya escrito texto en ella.

				
					using System;
using System.IO;
using System.Text.Json;

using JsonDocument documento = JsonDocument.Parse(File.ReadAllText("proyecto.json"));
JsonElement proyecto = documento.RootElement.GetProperty("proyecto");
foreach (JsonElement nivel in proyecto.GetProperty("niveles").EnumerateArray())
{
    foreach (JsonElement elemento in nivel.GetProperty("elementos").EnumerateArray())
    {
        string estado;
        if (!elemento.TryGetProperty("codigo", out JsonElement codigo))
            estado = "ausente";
        else if (codigo.ValueKind == JsonValueKind.Null)
            estado = "nulo";
        else if (codigo.ValueKind == JsonValueKind.String)
            estado = codigo.GetString() == "" ? "vacio" : "valor";
        else
            throw new InvalidDataException("codigo debe ser texto o null");

        string? nivelId = nivel.GetProperty("id").GetString();
        string? elementoId = elemento.GetProperty("id").GetString();
        Console.WriteLine($"{nivelId} | {elementoId} | {estado}");
    }
}
				
			

Ejecuta esta orden desde la carpeta que contiene el archivo de datos. Debes obtener las mismas tres líneas que con Python. El using del documento mantiene disponibles sus elementos durante el recorrido y libera después sus recursos. Si más adelante devuelves un JsonElement desde otro método, tendrás que cuidar la vida del documento que lo contiene; guardar una referencia a un elemento no lo independiza automáticamente de él.

				
					dotnet run --project LectorJson
				
			

La guía del modelo de documento JSON de .NET desarrolla estas opciones. Nuestro recorrido es deliberadamente visible: proyecto, niveles y elementos. No busca un código en cualquier rincón del documento ni completa con una lista vacía una propiedad estructural desaparecida. Si la forma cambia, prefiero enterarme a que el informe anuncie cero elementos con una serenidad que nadie le ha pedido.

Distingue un error de sintaxis de un contrato incumplido

Quita una coma entre dos propiedades del archivo y ejecuta de nuevo cualquiera de los lectores. El texto deja de tener una sintaxis JSON válida y la lectura falla: en Python verás un error de descodificación JSON; en C#, una excepción de JSON. Restaura la coma antes de la siguiente prueba. La posición indicada en el diagnóstico ayuda a localizar el problema, aunque el fallo pueda haberse originado justo antes del punto donde el lector lo detecta.

Después elimina la propiedad codigo de otra puerta, conservando comas y llaves correctas. El archivo seguirá siendo JSON válido y el programa mostrará ausente para esa puerta. Si escribes "codigo": false, también tendrás JSON válido, pero los ejemplos rechazarán ese tipo para el campo. Son tres situaciones diferentes: texto mal formado, propiedad opcional ausente y valor incompatible con el contrato. Meterlas en la misma categoría de «la API falla» solo aporta volumen a la discusión.

También puedes asignar " D01 " al código y observar valor. Eso significa que existe un texto, no que ese código cumpla un catálogo ni que debas recortar sus espacios. Esta distinción enlaza con los estados de datos ausentes, nulos y vacíos: primero conserva lo recibido y después aplica la regla que corresponda. Si necesitas rellenar un informe, guarda además el nivel y el identificador que explican a qué elemento pertenece cada resultado.

Cuando el JSON llegue de una API real tendrás que comprobar también el estado de la petición, los permisos, la paginación y el contrato de ese servicio. Un archivo local permite aprender el recorrido; no acredita esas otras piezas. Y una colección vacía puede necesitar una interpretación distinta según la operación que hayas solicitado. El paso útil ahora es poder decir qué has leído, dónde lo has encontrado y qué diferencia hay entre un dato que falta y otro que ha llegado nulo. Las llaves ya estaban ordenadas al principio; lo que hemos añadido es criterio para no perderse dentro.

Artículos relacionados

Respuestas

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *