[Raku] 如何處理 jsonlines 格式的檔案

作者:   發佈於: ,更新於:   #rakulang #jsonlines

jsonlines 是個利於處理大量同質資料的文字格式。基本上就是「每一行都是一個 JSON 文字格式的物件」而已。

例如說這樣就是個合格的 jsonlines 文字檔:

{"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]}
{"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]}
{"name": "May", "wins": []}
{"name": "Deloise", "wins": [["three of a kind", "5♣"]]}

只要每一行內容都是同質的,這種能夠內嵌複雜結構的檔案格式似乎比 CSV 稍微好用一點。至少,在碰到逗號、空白與引號時的處理規則就是依照 JSON 語法來處理就好。

要處理這種格式,就是要在讀檔時一行一行地讀,然後逐一交給某個能夠解析 JSON 文字的函式來將單行文字轉換成為物件。慣例上來說通常是轉換為 Hash 或 Array。

在 Raku 語裡面要一行一行讀檔就是在檔名字串後方加上 .IO.lines 造出一個迭代器。而既然有了迭代器,就可以使用 .map

use JSON::Fast;

my @records = "example.jsonl".IO.lines.map(&from-json);

如果是要反過來,把一個 Array 中的很多物件做成 jsonlines 文字,就是改用 to-json。但是要注意,不能使其產生帶換行字符與排版用的空白字符,而必需採用所謂的「醜醜的」,把所有內容全部塞在一行之內的輸出效果。

為了方便起見,先利用 JSON::Fastto-json 函式來定義一個 to-jsonline 函式,並使其能將物件 $o 轉換成一行 JSON 格式的文字,且在最後補上一個換行字符。

要將做好的文字全數輸出到某個檔案,就使用 .IO.spurt

整合起來範例如下:

use JSON::Fast;

sub to-jsonline ($o) {
    to-json($o, :!pretty, :sorted-keys) ~ "\n"
}

my @records = [
    {"name" => "Gilbert", "wins" => [["straight", "7♣"], ["one pair", "10♥"]]},
    {"name" => "Alexa", "wins" => [["two pair", "4♠"], ["two pair", "9♠"]]},
    {"name" => "May", "wins" => []},
    {"name" => "Deloise", "wins" => [["three of a kind", "5♣"]]},
];

my $jsonlines = @records.map(&to-jsonline).join("");

"output.jsonl".IO.spurt($jsonlines);