Extraction de JSON à partir d'une requête client HTTP Raku
J'ai du mal à comprendre ce qui ne va pas avec ce code Raku.
Je souhaite récupérer JSON à partir d'un site Web et imprimer un champ de chaque élément d'un tableau dans le JSON (dans ce cas, les titres des derniers sujets de n'importe quel forum Discourse).
C'est du code que je m'attendais à fonctionner, mais il a échoué:
use HTTP::UserAgent;
use JSON::Tiny;
my $client = HTTP::UserAgent.new; $client.timeout = 10;
my $url = 'https://meta.discourse.org/latest.json'; my $resp = $client.get($url);
my %data = from-json($resp.content); # I think the problem starts here. my @topics = %data<topic_list><topics>; say @topics.WHAT; #=> (Array) for @topics -> $topic {
say $topic<fancy_title>;
}
Le message d'erreur provient de la say $topic<fancy_title>
ligne:
Type Array does not support associative indexing.
in block <unit> at http-clients/http.raku line 18
Je m'attendais à ce que cela $topic
soit écrit comme %topic
, car c'est un tableau de hachages, mais cela ne fonctionne pas:
for @topics -> %topic {
say %topic<fancy_title>;
}
Le message d'erreur pour cela est:
Type check failed in binding to parameter '%topic'; expected Associative but got Array ([{:archetype("regula...)
in block <unit> at http-clients/http.raku line 17
Si vous inspectez les données, il doit s'agir d'un hachage et non d'un tableau. J'ai essayé @array
mais je sais que ce n'est pas correct, alors j'ai changé %topic
pour $topic
.
Je l'ai finalement fait fonctionner en ajoutant .list
à la ligne qui définit @topics
mais je ne comprends pas pourquoi cela le corrige, car @topics
c'est un (Array)
ajout ou non.
Voici le code de travail:
use HTTP::UserAgent;
use JSON::Tiny;
my $client = HTTP::UserAgent.new; $client.timeout = 10;
my $url = 'https://meta.discourse.org/latest.json'; my $resp = $client.get($url);
my %data = from-json($resp.content); # Adding `.list` here makes it work, but the type doesn't change. # Why is `.list` needed? my @topics = %data<topic_list><topics>.list; say @topics.WHAT; #=> (Array) # Why is it `$topic` instead of `%topic`?
for @topics -> $topic { say $topic<fancy_title>;
}
Quelqu'un sait-il pourquoi il échoue et la bonne façon d'effectuer cette tâche?
Réponses
Ce qui s'est passé, c'est que vous avez créé un tableau d'un tableau lorsque vous dites
my @topics = %data<topic_list><topics>;
Ce n'est pas unique à ces modules, mais général à travers Raku avec les attributions de baies.
Prenons un hachage plus simple pour voir ce qui se passe:
my %x = y => [1,2,3];
my $b = %x<y>; my @b = %x<y>; say $b; # [1 2 3]
say @b; # [[1 2 3]]
Le hic, c'est que l'affectation de tableau (qui est utilisée lorsque la variable a le @
sigil) interprète %x<y>
comme un élément unique car il est dans un conteneur scalaire, qu'il place ensuite avec plaisir @b[0]
. Bien que vous ne puissiez pas contrôler le module lui-même, vous pouvez voir la différence dans mon exemple si vous dites my %x is Map = …
que Map
ne placez pas les éléments dans des conteneurs scalaires, mais que les Hash
objets le font. Il y a deux façons de dire à Raku de traiter l'élément unique comme son contenu, plutôt que comme un seul conteneur.
- Liez le tableau
Au lieu d'utiliser@b = %x<y>
, vous utilisez@b := %x<y>
. La liaison aux@
variables -sigiled se décontainérise automatiquement. - Utiliser un opérateur zen
Lorsque vous voulez éviter la possibilité qu'une liste / valeur de hachage soit traitée comme une seule, vous pouvez utiliser une tranche zen pour supprimer tout conteneur s'il y en a. Cela peut être fait soit à affectation (@b = %x<y>[]
), soit à lafor
boucle (for @b[] -> $b
). Notez que<>
,[]
et{}
sont en fait synonymes, quel que soit le type réel - la plupart des gens utilisent simplement celui qui correspond au précédent.
Donc, dans votre code, vous pouvez simplement faire:
...
my %data = from-json($resp.content);
my @topics := %data<topic_list><topics>; # (option 1) binding
my @topics = %data<topic_list><topics><>; # (option 2) zen slice
for @topics -> $topic { say $topic<fancy_title>;
}
Ou dans votre boucle, comme option 3:
for @topics<> -> $topic { say $topic<fancy_title>;
}
La raison pour laquelle les .list
choses corrigent - comme vous pouvez probablement le supposer après le reste de la réponse - est qu'elle renvoie une nouvelle liste qui n'est pas dans un conteneur.