Extrahieren von JSON aus einer Raku-HTTP-Clientanforderung
Ich habe Probleme zu verstehen, was mit diesem Raku-Code nicht stimmt.
Ich möchte JSON von einer Website abrufen und ein Feld von jedem Element in einem Array innerhalb des JSON ausdrucken (in diesem Fall die Titel der neuesten Themen aus einem beliebigen Diskursforum).
Dies ist Code, von dem ich erwartet hatte, dass er funktioniert, aber er ist fehlgeschlagen:
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>;
}
Die Fehlermeldung stammt aus der say $topic<fancy_title>
Zeile:
Type Array does not support associative indexing.
in block <unit> at http-clients/http.raku line 18
Ich hätte erwartet, dass $topic
das so geschrieben werden sollte %topic
, weil es eine Reihe von Hashes ist, aber das funktioniert nicht:
for @topics -> %topic {
say %topic<fancy_title>;
}
Die Fehlermeldung dafür lautet:
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
Wenn Sie die Daten überprüfen, sollte es sich um einen Hash und nicht um ein Array handeln. Ich habe versucht , @array
aber ich weiß , dass nicht richtig ist, also habe ich %topic
zu $topic
.
Ich habe es endlich zum Laufen gebracht, indem ich es zu .list
der definierten Zeile hinzugefügt habe, @topics
aber ich verstehe nicht, warum das das Problem behebt, weil @topics
es darum geht, (Array)
ob das hinzugefügt wird oder nicht.
Dies ist der Arbeitscode:
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>;
}
Weiß jemand, warum es fehlschlägt und wie diese Aufgabe richtig ausgeführt wird?
Antworten
Was passiert ist, ist, dass Sie ein Array eines Arrays erstellt haben, wenn Sie sagen
my @topics = %data<topic_list><topics>;
Dies gilt nicht nur für diese Module, sondern allgemein für Raku mit Array-Zuweisungen.
Nehmen wir einen einfacheren Hash, um zu sehen, was los ist:
my %x = y => [1,2,3];
my $b = %x<y>; my @b = %x<y>; say $b; # [1 2 3]
say @b; # [[1 2 3]]
Der Haken ist, dass die Array-Zuweisung (die verwendet wird, wenn die Variable das @
Siegel hat) %x<y>
als einzelnes Element interpretiert wird , da es sich in einem skalaren Container befindet, den sie dann glücklich einfügt @b[0]
. Während Sie das Modul selbst nicht steuern können, können Sie den Unterschied in meinem Beispiel sehen, wenn Sie sagen my %x is Map = …
, dass Map
Sie keine Elemente in skalaren Containern platzieren, sondern Hash
Objekte. Es gibt zwei Möglichkeiten, Raku anzuweisen, das einzelne Objekt als Inhalt und nicht als einzelnen Container zu behandeln.
- Binden Sie das Array
Anstatt zu verwenden@b = %x<y>
, verwenden Sie@b := %x<y>
. Die Bindung an@
-sigiled-Variablen wird automatisch dekontainiert. - Verwenden eines Zen-Operators
Wenn Sie vermeiden möchten, dass ein Listen- / Hash-Wert als einer behandelt wird, können Sie einen Zen-Slice verwenden, um einen Container zu entfernen, falls dieser vorhanden ist. Dies kann entweder bei Zuweisung (@b = %x<y>[]
) oder beifor
Schleife (for @b[] -> $b
) erfolgen. Beachten Sie, dass<>
,[]
und{}
sind unabhängig vom tatsächlichen Typ auch synonym - die meisten Leute verwenden nur den, der dem vorherigen entspricht.
In Ihrem Code können Sie also einfach Folgendes tun:
...
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>;
}
Oder in Ihrer Schleife als Option 3:
for @topics<> -> $topic { say $topic<fancy_title>;
}
Der Grund für die .list
Korrekturen - wie Sie wahrscheinlich nach dem Rest der Antwort vermuten können - ist, dass eine neue Liste zurückgegeben wird, die sich nicht in einem Container befindet.