Translate

domingo, 1 de febrero de 2026

JSON Decoders de elm



El paquete elm/json nos proporciona el módulo Json.Decode. Está lleno de pequeños decodificadores que podemos conectar.

Para obtener "edad" de { "nombre": "Tom", "edad": 42 }, crearíamos un decodificador como este:


import Json.Decode exposing (Decoder, field, int)


ageDecoder : Decoder Int

ageDecoder =

  field "age" int


 -- int : Decoder Int

 -- field : String -> Decoder a -> Decoder a


La función campo toma dos argumentos:

String: el nombre del campo. Por lo tanto, solicitamos un objeto con un campo "edad".

Decoder a: el decodificador que se probará a continuación. Si el campo "edad" existe, probaremos este decodificador con el valor que contiene.

En resumen, el campo "edad" int solicita un campo "edad" y, si existe, ejecuta el Decoder Int para intentar extraer un entero.

Hacemos prácticamente lo mismo para extraer el campo "nombre":


import Json.Decode exposing (Decoder, field, string)


nameDecoder : Decoder String

nameDecoder =

  field "name" string


-- string : Decoder String


En este caso, solicitamos un objeto con un campo "nombre" y, si existe, queremos que su valor sea una cadena.


¿Y si queremos decodificar dos campos? Unimos los decodificadores con map2:


map2 : (a -> b -> value) -> Decoder a -> Decoder b -> Decoder value

Esta función toma dos decodificadores. Los prueba y combina sus resultados. Ahora podemos combinar dos decodificadores diferentes:


import Json.Decode exposing (Decoder, map2, field, string, int)


type alias Person =

  { name : String

  , age : Int

  }


personDecoder : Decoder Person

personDecoder =

  map2 Person

      (field "name" string)

      (field "age" int)


Por lo tanto, si usáramos personDecoder en { "name": "Tom", "age": 42 }, obtendríamos un valor Elm como Person "Tom" 42.

Si realmente quisiéramos adentrarnos en la esencia de los decodificadores, definiríamos personDecoder como map2 Person nameDecoder ageDecoder utilizando nuestras definiciones anteriores. ¡Siempre conviene construir los decodificadores a partir de bloques más pequeños!

Muchos datos JSON no son tan claros ni planos. Imagina si /api/random-quotes/v2 se hubiera publicado con información más completa sobre los autores:


{

  "quote": "December used to be a month but it is now a year",

  "source": "Letters from a Stoic",

  "author":

  {

    "name": "Seneca",

    "age": 68,

    "origin": "Cordoba"

  },

  "year": 54

}

Podríamos gestionar este nuevo escenario anidando nuestros decodificadores:

import Json.Decode exposing (Decoder, map2, map4, field, int, string)


type alias Quote =

  { quote : String

  , source : String

  , author : Person

  , year : Int

  }


quoteDecoder : Decoder Quote

quoteDecoder =

  map4 Quote

    (field "quote" string)

    (field "source" string)

    (field "author" personDecoder)

    (field "year" int)


type alias Person =

  { name : String

  , age : Int

  }


personDecoder : Decoder Person

personDecoder =

  map2 Person

    (field "name" string)

    (field "age" int)


Observe que no nos molestamos en decodificar el campo "origen" del autor. Los decodificadores no tienen ningún problema en omitir campos, lo que puede ser útil al extraer poca información de valores JSON muy grandes.