从RakuHTTP客户端请求中提取JSON

我无法理解这个 Raku 代码有什么问题。

我想从网站获取 JSON,并从 JSON 数组中的每个项目中打印出一个字段(在这种情况下,是任何 Discourse 论坛中最新主题的标题)。

这是我希望工作的代码,但它失败了:

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>;
}

错误消息来自以下say $topic<fancy_title>行:

Type Array does not support associative indexing.
  in block <unit> at http-clients/http.raku line 18

我原以为$topic应该写为%topic,因为它是一个哈希数组,但这不起作用:

for @topics -> %topic {
    say %topic<fancy_title>;
}

错误消息是:

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

如果您检查数据,它应该是一个散列,而不是一个数组。我试过@array,但我知道这是不正确的,所以我改变%topic$topic

我终于它得到的工作,加入.list到行定义@topics,但我不明白为什么修复它,因为@topics是一个(Array)是否被添加或不。

这是工作代码:

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>;
}

有谁知道它为什么失败以及执行此任务的正确方法?

回答

发生的事情是你创建了一个数组的数组,当你说

my @topics = %data<topic_list><topics>;

这不是这些模块独有的,而是在 Raku 中通过数组分配普遍存在的。

让我们用一个更简单的哈希来看看发生了什么:

my %x = y => [1,2,3];

my $b = %x<y>;
my @b = %x<y>;

say $b;  # [1 2 3]
say @b;  # [[1 2 3]]

问题在于数组赋值(当变量具有@sigil 时使用)将其解释%x<y> 为单个项目,因为它位于标量容器中,然后它很高兴地将其放入@b[0]. 虽然你无法控制模块本身,你可以看到其中的差别在我的例子,如果你说的my %x is Map = …Map不要放置在容器标项目,但Hash对象一样。有两种方法可以告诉 Raku 将单个项目视为其内容,而不是单个容器。

  • 绑定数组
    而不是使用@b = %x<y>,您使用@b := %x<y>. 绑定到@-sigiled 变量会自动解除容器化。
  • 使用 zen 运算符
    当您想避免将列表/哈希值视为一个的可能性时,您可以使用 zen 切片来删除任何容器(如果它碰巧在那里)。这是可以做到无论是在分配(@b = %x<y>[]),for环(for @b[] -> $b)。请注意<>[], 和{}是有效的同义词,无论实际类型如何——大多数人只使用与前一个匹配的那个。

所以在你的代码中,你可以这样做:

...
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>;
}

或者在您的循环中,作为选项 3:

for @topics<> -> $topic {
    say $topic<fancy_title>;
}

.list修复问题的原因- 正如您在回答其余部分之后可能猜测的那样 - 是它返回一个不在容器中的新列表。

  • Thanks. I actually did do something like `%data<topic_list><topics>[]` at one point due to a typo (must have not pressed `0` hard enough) and it worked, but I thought that it was an accidental quirk of the language that probably wasn't the right way to do things. 🙂 I'll experiment with both of the methods you mentioned.
  • @raiph decont is necessary if you're doing directly to the `for` loop in some cases. I know I've run into it once or twice where I *had* to use `for foo.bar(…)<> { … }`. But honestly, using some of other techniques can make code more explicit, particularly the `.list`

以上是从RakuHTTP客户端请求中提取JSON的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>