#!/usr/bin/env catnip
# Requêtage JSON avec JMESPath
# JMESPath est un langage de requête pour JSON (utilisé par AWS CLI)
jmespath = import('jmespath')
# Struct pour encapsuler une requête nommée
struct Query {
label; expr;
run(self, data) => { jmespath.search(self.expr, data) }
}
show = (label, result) => { print(f" {label} : {result}") }
# Données de test (réponse API d'exemple)
data = dict(
users=list(dict(
id=1,
name="Alice",
email="alice@example.com",
role="admin",
active=True,
projects=list("alpha", "beta"),
), dict(
id=2,
name="Bob",
email="bob@example.com",
role="developer",
active=True,
projects=list("beta", "gamma"),
), dict(
id=3,
name="Charlie",
email="charlie@example.com",
role="developer",
active=False,
projects=list("alpha"),
)),
metadata=dict(
total=3,
page=1,
per_page=10,
)
)
# Accès simple
print("⇒ Accès simple")
simple_queries = list(
Query("metadata.total", "metadata.total"),
Query("metadata.page", "metadata.page"),
)
simple_queries.[(q) => { show(q.label, q.run(data)) }]
# Accès aux listes
print()
print("⇒ Accès aux listes")
list_queries = list(
Query("Premier user", "users[0].name"),
Query("Dernier user", "users[-1].name"),
)
list_queries.[(q) => { show(q.label, q.run(data)) }]
# Projection (extraire un champ de chaque élément)
print()
print("⇒ Projection")
proj_queries = list(
Query("Tous les noms", "users[*].name"),
Query("Tous les emails", "users[*].email"),
)
proj_queries.[(q) => { show(q.label, q.run(data)) }]
# Filtrage
print()
print("⇒ Filtrage")
filter_queries = list(
Query("Admins", "users[?role=='admin'].name"),
Query("Users actifs", "users[?active==`true`].name"),
)
filter_queries.[(q) => { show(q.label, q.run(data)) }]
devs = Query("Developers", "users[?role=='developer']").run(data)
show("Developers (count)", len(devs))
# Projection multiple (dict construit à la volée)
print()
print("⇒ Projection multiple (sélection de champs)")
subset = Query("Subset", "users[*].{nom: name, mail: email}").run(data)
print(" Subset:")
for u in subset {
print(f" {u}")
}
print(" Exemple premier élément:")
show("nom", subset[0]['nom'])
show("mail", subset[0]['mail'])
# Flatten (aplatir les listes)
print()
print("⇒ Flatten")
all_projects = Query("Projets", "users[*].projects[]").run(data)
show("Tous les projets (avec doublons)", all_projects)
# Fonctions JMESPath
print()
print("⇒ Fonctions JMESPath")
fn_queries = list(
Query("Nombre d'users", "length(users)"),
Query("Premier nom (sort)", "sort(users[*].name)[0]"),
Query("Dernier nom (sort)", "sort(users[*].name)[-1]"),
)
fn_queries.[(q) => { show(q.label, q.run(data)) }]
# Conditions complexes
print()
print("⇒ Conditions complexes")
beta_users = Query("Users sur projet beta", "users[?contains(projects, 'beta')].name").run(data)
show("Users sur projet beta", beta_users)
# Dict imbriqué dans le résultat
print()
print("⇒ Dict imbriqué (retour structuré)")
summary = Query("Résumé", "{meta: metadata, admins: users[?role=='admin'].name, total_users: length(users)}").run(
data
)
show("Résumé", summary)
# Exemple: réponse API paginée
api_response = dict(
items=list(
dict(sku="A001", price=29.99, stock=100),
dict(sku="A002", price=49.99, stock=0),
dict(sku="B001", price=19.99, stock=50),
dict(sku="B002", price=99.99, stock=25),
),
pagination=dict(
next="/api/items?page=2",
total_pages=5,
)
)
print()
print("⇒ Exemple API e-commerce")
ecom_queries = list(
Query("SKUs en stock", "items[?stock > `0`].sku"),
Query("Produits > 50 EUR", "items[?price > `50`].{sku: sku, prix: price}")
)
ecom_queries.[(q) => { show(q.label, q.run(api_response)) }]
# Min/Max avec sort
print()
print("⇒ Min/Max")
cheapest = Query("Moins cher", "sort_by(items, &price)[0]").run(api_response)
most_expensive = Query("Plus cher", "sort_by(items, &price)[-1]").run(api_response)
show("Moins cher", f"{cheapest['sku']} - {cheapest['price']} EUR")
show("Plus cher", f"{most_expensive['sku']} - {most_expensive['price']} EUR")
# Compilation pour performance
print()
print("⇒ Expression compilée")
expr = jmespath.compile("users[?active].name")
result = expr.search(data)
show("Résultat", result)