Crystal-lang: JSON đệ quy hoặc Hash
Tôi đang cố tạo JSON hoặc Hash có thể có độ sâu N. Ví dụ: X những người có tên duy nhất có thể có Y trẻ và những trẻ đó có thể có Z trẻ (và tiếp tục cho đến N thế hệ). Tôi muốn tạo một Hash (hoặc JSON) trông giống như sau:
{
"John" => {
"Lara" => {
"Niko" => "Doe"
},
"Kobe" => "Doe"
},
"Jess" => {
"Alex" => "Patrik"
}
}
Tôi đã thử làm việc với bí danh đệ quy nhưng không thể đạt được điều đó.
alias Person = Hash(String, Person) | Hash(String, String)
Đầu vào có thể đến từ các mảng Chuỗi như
["John|Lara|Niko", "John|Kobe", "Jess|Alex"]
["Doe", "Patrik"]
(Tôi có thể giải quyết các vòng lặp. Vấn đề của tôi là thêm chúng vào Hash vì kích thước của chúng không xác định.)
Tôi đã xem qua cuộc thảo luận này https://forum.crystal-lang.org/t/how-do-i-create-a-nested-hash-type/885 nhưng tiếc là tôi không thể đạt được những gì tôi muốn và cũng giữ các phương thức của Hash (hoặc JSON) (cần thiết).
Trả lời
Tôi không thể xác định rõ làm thế nào bạn đến được kết quả ví dụ từ đầu vào ví dụ của bạn, vì vậy tôi sẽ sử dụng một thiết lập khác: Giả sử chúng ta có một định dạng tệp cấu hình đơn giản trong đó các khóa được cấu trúc và nhóm thông qua một chuỗi chấm và tất cả các giá trị luôn là chuỗi.
app.name = test
app.mail.enable = true
app.mail.host = mail.local
server.host = localhost
server.port = 3000
log_level = debug
Chúng ta có thể phân tích cú pháp nó thành một đệ quy Hashnhư vậy:
alias ParsedConfig = Hash(String, ParsedConfig)|String
config = Hash(String, ParsedConfig).new
# CONFIG being our input from above
CONFIG.each_line do |entry|
keys, value = entry.split(" = ")
keys = keys.split(".")
current = config
keys[0..-2].each do |key|
if current.has_key?(key)
item = current[key]
if item.is_a?(Hash)
current = item
else
raise "Malformed config"
end
else
item = Hash(String, ParsedConfig).new
current[key] = item
current = item
end
end
current[keys.last] = value
end
pp! config
Đầu ra sẽ là:
config # => {"app" =>
{"name" => "test", "mail" => {"enable" => "true", "host" => "mail.local"}},
"server" => {"host" => "localhost", "port" => "3000"},
"log_level" => "debug"}
Ngoài ra, chúng ta có thể phân tích cú pháp nó thành một cấu trúc đệ quy:
record ConfigGroup, entries = Hash(String, ConfigGroup|String).new
config = ConfigGroup.new
# CONFIG being our input from above
CONFIG.each_line do |entry|
keys, value = entry.split(" = ")
keys = keys.split(".")
current = config
keys[0..-2].each do |key|
if current.entries.has_key?(key)
item = current.entries[key]
if item.is_a?(ConfigGroup)
current = item
else
raise "Malformed config"
end
else
item = ConfigGroup.new
current.entries[key] = item
current = item
end
end
current.entries[keys.last] = value
end
pp! config
Đầu ra sau đó sẽ là:
config # => ConfigGroup(
@entries=
{"app" =>
ConfigGroup(
@entries=
{"name" => "test",
"mail" =>
ConfigGroup(@entries={"enable" => "true", "host" => "mail.local"})}),
"server" => ConfigGroup(@entries={"host" => "localhost", "port" => "3000"}),
"log_level" => "debug"})
Cấu trúc đệ quy hiện tại ít lỗi hơn một chút, cung cấp một vị trí tốt cho các phương thức tùy chỉnh trên các đối tượng miền được phân tích cú pháp của bạn và thường có một tương lai chắc chắn hơn so với bí danh đệ quy, đôi khi có một chút lỗi.
Ví dụ đầy đủ trên carc.in: https://carc.in/#/r/9mxr