dev-resources.site
for different kinds of informations.
Code. Gleam. Extract fields from JSON
Published at
12/18/2024
Categories
gleamlang
json
webdev
Author
axkira
Author
6 person written this
axkira
open
Hi!
Sometimes we need to get just a couple of fields from a JSON string without any complex stuff like writing decoders or using a JSON-schema.
We will use
result.then()
for easy piping: if the result, passed tothen()
isOk
, then we continue with it; if the result isError
- we stop there and return this error from the pipe. So it's important to have consistentError
types through the whole pipe.
So, here are the steps.
- Install gleam_json package:
# Erlang version <= OTP26
gleam add gleam_json@1
# Erlang version >= OTP27
gleam add gleam_json@2
- Write some types in
types.gleam
:
pub type HttpError {
// ...
JsonParseError(error: JsonParseErrorType, field: String, json_string: String)
}
pub type JsonParseErrorType {
InvalidJsonForParsing
ObjectFieldNotFound
IntegerFieldNotFound
FloatFieldNotFound
StringFieldNotFound
SeveralFieldsNotFound
}
- Write some very basic utils to shorten the
gleam_json
usage syntax and simplify piping process. Here I've added only some funs for parsing root JSON as an object (it can also be an array) and extracting integers, floats and strings (it won't be hard to add other ones by yourself):
import gleam/string
import gleam/result.{then, replace_error}
import gleam/json
import gleam/dynamic
import gleam/dict
import types.{
type HttpError, JsonParseError,
InvalidJsonForParsing,
ObjectFieldNotFound,
IntegerFieldNotFound,
FloatFieldNotFound,
StringFieldNotFound,
}
/// Parses JSON string as an object into a dictionary.
pub fn parse_obj(json_string: String) -> Result(dict.Dict(String, dynamic.Dynamic), HttpError) {
json_string
|> json.decode(dynamic.dict(dynamic.string, dynamic.dynamic))
|> replace_error(JsonParseError(error: InvalidJsonForParsing, field: "", json_string: json_string))
}
/// Retrieves an object field from the current JSON level.
pub fn get_obj(
body: dict.Dict(String, dynamic.Dynamic),
field: String,
) -> Result(dict.Dict(String, dynamic.Dynamic), HttpError) {
body
|> dict.get(field)
|> then(as_dict())
|> replace_error(JsonParseError(error: ObjectFieldNotFound, field: field, json_string: string.inspect(body)))
}
/// Retrieves an integer field from the current JSON level.
pub fn get_int(
body: dict.Dict(String, dynamic.Dynamic),
field: String,
) -> Result(Int, HttpError) {
body
|> dict.get(field)
|> then(as_int())
|> replace_error(JsonParseError(error: IntegerFieldNotFound, field: field, json_string: string.inspect(body)))
}
/// Retrieves a float field from the current JSON level.
pub fn get_float(
body: dict.Dict(String, dynamic.Dynamic),
field: String,
) -> Result(Float, HttpError) {
body
|> dict.get(field)
|> then(as_float())
|> replace_error(JsonParseError(error: FloatFieldNotFound, field: field, json_string: string.inspect(body)))
}
/// Retrieves a string field from the current JSON level.
pub fn get_string(
body: dict.Dict(String, dynamic.Dynamic),
field: String,
) -> Result(String, HttpError) {
body
|> dict.get(field)
|> then(as_string())
|> replace_error(JsonParseError(error: StringFieldNotFound, field: field, json_string: string.inspect(body)))
}
/// Replacement for `dynamic.dict(dynamic.string, dynamic.dynamic)` to have a custom `Error` for piping.
fn as_dict() -> fn(dynamic.Dynamic) -> Result(dict.Dict(String, dynamic.Dynamic), Nil) {
fn(body: dynamic.Dynamic) {
body
|> dynamic.dict(dynamic.string, dynamic.dynamic)
|> replace_error(Nil)
}
}
/// Replacement for `dynamic.int(_)` to have a custom `Error` for piping.
fn as_int() -> fn(dynamic.Dynamic) -> Result(Int, Nil) {
fn(body: dynamic.Dynamic) {
body
|> dynamic.int()
|> replace_error(Nil)
}
}
/// Replacement for `dynamic.float(_)` to have a custom `Error` for piping.
fn as_float() -> fn(dynamic.Dynamic) -> Result(Float, Nil) {
fn(body: dynamic.Dynamic) {
body
|> dynamic.float()
|> replace_error(Nil)
}
}
/// Replacement for `dynamic.string(_)` to have a custom `Error` for piping.
fn as_string() -> fn(dynamic.Dynamic) -> Result(String, Nil) {
fn(body: dynamic.Dynamic) {
body
|> dynamic.string()
|> replace_error(Nil)
}
}
- Use your new utils:
pub fn main() {
// {
// "name": "Lucy",
// "stats": {
// "class": "Barbarian",
// "power": 6,
// "max_hp": 10
// },
// "pets": {
// "Wolfie": {
// "type": "dog"
// }
// }
// }
let json_string = "{\"name\": \"Lucy\",\"stats\": {\"class\": \"Barbarian\",\"power\": 6,\"max_hp\": 10},\"pets\": {\"Wolfie\": {\"type\": \"dog\"}}}"
let json_dict = json_string |> json.parse_obj()
// Get Lucy's name
json_dict
|> then(json.get_string(_, "name"))
|> io.debug()
// Ok("Lucy")
// Get Wolfie's type
json_dict
|> then(json.get_obj(_, "pets"))
|> then(json.get_obj(_, "Wolfie"))
|> then(json.get_string(_, "type"))
|> io.debug()
// Ok("dog")
// Get something ridiculous
// Note that we get an error on extracting the `nonsense` field and don't go to `type` out of the box
json_dict
|> then(json.get_obj(_, "stats"))
|> then(json.get_obj(_, "nonsense"))
|> then(json.get_string(_, "type"))
|> io.debug()
// Error(JsonParseError(ObjectFieldNotFound, "nonsense", "dict.from_list([#(\"class\", \"Barbarian\"), #(\"max_hp\", 10), #(\"power\", 6)])"))
// Get something even more ridiculous
// Now we handle both fields and raising a `SeveralFieldsNotFound` error
let nonesense =
json_dict
|> then(json.get_obj(_, "stats"))
|> then(json.get_string(_, "nonesense"))
let delirious =
json_dict
|> then(json.get_obj(_, "stats"))
|> then(json.get_int(_, "delirious"))
case nonesense, delirious {
Ok(nonesense), Ok(delirious) -> Ok(#(nonesense, delirious))
Error(_), Error(_) -> Error(JsonParseError(error: SeveralFieldsNotFound, field: "stats.{nonesense, delirious}", json_string: json_string))
Error(err), _ -> Error(err)
_, Error(err) -> Error(err)
}
|> io.debug()
// Error(JsonParseError(SeveralFieldsNotFound, "stats.{nonesense, delirious}", "{\"name\": \"Lucy\",\"stats\": {\"class\": \"Barbarian\",\"power\": 6,\"max_hp\": 10},\"pets\": {\"Wolfie\": {\"type\": \"dog\"}}}"))
}
Bye!
P.S.
Sorry for poor syntax highlighting. Gleam isn't supported and I've chosen Erlang. That's better than a plain text, I guess π€·ββοΈ
json Article's
30 articles in total
How to Fetch URL Content, Set It into a Dictionary, and Extract Specific Keys in iOS Shortcuts
read article
Dynamic Routes in Astro (+load parameters from JSON)
read article
Effortlessly Host Static JSON Files with JSONsilo.com
read article
How to Implement Authentication in React Using JWT (JSON Web Tokens)
read article
Converting documents for LLM processing β A modern approach
read article
Import JSON Data in Astro (with Typescript)
read article
Devise not accepting JSON Token
read article
Integration for FormatJS/react-intl: Automated Translations with doloc
read article
βDefuβ usage in unbuild source code.
read article
Converting documents for LLM processing β A modern approach
read article
How to crawl and parse JSON data with Python crawler
read article
JSON Visual Edit
read article
Develop a ulauncher extension with a command database
read article
Building a Smart Feedback Agent with Copilot Studio, Adaptive cards and Power Automate
read article
Simplifying JSON Validation with Ajv (Another JSON Validator)
read article
A Straightforward Guide to Building and Using aΒ JSON Database File
read article
AI prompt sample - a full chat content that demonstrates how to get a professional looking website in a few munities
read article
Fixing and Validating JSON with Ease: An In-Depth Guide
read article
Useful too to work with your JSON files - jq
read article
what is jq? a program for json files
read article
Code. Gleam. Extract fields from JSON
currently reading
Build an Application Without SQL Server Database (Avoiding RPrometheusedis, MongoDB, and )
read article
FAQ β Bloomer Mock Data Generator
read article
My React Journey: Day 18
read article
Working with JSON in MySQL
read article
JSON for Biggners
read article
angular and json
read article
iter.json: A Powerful and Efficient Way to Iterate and Manipulate JSON in Go
read article
This unknown Currency API is served over 50 Billion times a month !
read article
Common Data Formats in JavaScript: A Comprehensive Guide With Examples
read article
Featured ones: