从 Raku HTTP 客户端请求中提取 JSON [英] Extracting JSON from a Raku HTTP client request

查看:42
本文介绍了从 Raku HTTP 客户端请求中提取 JSON的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm having trouble understanding what is wrong with this Raku code.

我想从网站获取 JSON,并从 JSON 数组中的每个项目中打印出一个字段(在本例中为来自任何 Discourse 论坛的最新主题的标题).

I want to fetch JSON from a website, and print out a field from each item in an array within the JSON (in this case the titles of latest topics from any Discourse forum).

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

This is code that I expected to work, but it failed:

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 行:

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

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

I would have expected that $topic should be written as %topic, because it's an array of hashes, but this doesn't work:

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.

If you inspect the data, it should be a hash, not an array. I tried @array but I know that isn't correct, so I changed %topic to $topic.

我终于通过将 .list 添加到定义 @topics 的行中使其工作,但我不明白为什么会修复它,因为 @topic 是一个 (Array),无论是否添加.

I finally got it to work by adding .list to the line that defines @topics but I don't understand why that fixes it, because @topics is an (Array) whether that is added or not.

这是工作代码:

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

有谁知道它失败的原因以及执行此任务的正确方法吗?

Does anyone know why it's failing and the correct way to perform this task?

推荐答案

发生的事情是你说的时候创建了一个数组的数组

What's happened is that you've created an array of an array when you say

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

这不是这些模块独有的,而是通过数组分配在 Raku 中通用的.

This isn't unique to these modules, but general across Raku with array assignments.

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

Let's take a simpler hash to see what's going on:

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

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

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

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

The catch is that the array assignment (which is used when the variable has the @ sigil) interprets %x<y> as a single item as it's in a scalar container, which it then happily puts in @b[0]. While you can't control the module itself, you can see the difference in my example if you say my %x is Map = … as Map do not place items in scalar containers, but Hash objects do. There are two ways to tell Raku to treat the single item as its contents, rather than a single container.

  • 绑定数组
    不使用 @b = %x,而是使用 @b := %x.绑定到 @-sigiled 变量会自动解除容器化.
  • 使用禅宗运算符
    当您想避免将列表/哈希值视为一个的可能性时,您可以使用 zen 切片来删除任何碰巧在那里的容器.这可以要么在赋值(@b = %x[])for> 循环(for @b[] -> $b).请注意,<>[]{} 实际上是同义词,无论实际类型如何——大多数人只使用那些匹配上一个.
  • Bind the array
    Instead of using @b = %x<y>, you use @b := %x<y>. Binding to @-sigiled variables decontainerizes automatically.
  • Use a zen operator
    When you want to avoid the possibility of a list/hash value being treated as one, you can use a zen slice to remove any container if it happens to be there. This can be done either at assignment (@b = %x<y>[]) or at the for loop (for @b[] -> $b). Note that <>, [], and {} are effectively synonymous, regardless the actual type — most people just use the one that matches the previous.

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

So in your code, you could just do:

...
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:

Or in your loop, as option 3:

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

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

The reason that the .list fixes things — as you can probably surmise after the rest of the answer — is that it returns a fresh list that isn't in a container.

这篇关于从 Raku HTTP 客户端请求中提取 JSON的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆