Extrahieren von JSON aus einer Raku-HTTP-Clientanforderung

Nov 26 2020

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 $topicdas 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 , @arrayaber ich weiß , dass nicht richtig ist, also habe ich %topiczu $topic.

Ich habe es endlich zum Laufen gebracht, indem ich es zu .listder definierten Zeile hinzugefügt habe, @topicsaber ich verstehe nicht, warum das das Problem behebt, weil @topicses 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

8 user0721090601 Nov 26 2020 at 07:06

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 MapSie keine Elemente in skalaren Containern platzieren, sondern HashObjekte. 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 bei forSchleife ( 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 .listKorrekturen - 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.