Crystal-lang: Recursive JSON หรือ Hash
ฉันกำลังพยายามสร้าง JSON หรือ Hash ที่มีความลึก N ตัวอย่าง: X คนที่มีชื่อไม่ซ้ำกันอาจมีลูก Y และเด็กเหล่านั้นอาจมีลูก Z (และมีต่อไปจนถึง N ชั่วอายุคน) ฉันต้องการสร้าง Hash (หรือ JSON) ที่มีลักษณะดังนี้:
{
"John" => {
"Lara" => {
"Niko" => "Doe"
},
"Kobe" => "Doe"
},
"Jess" => {
"Alex" => "Patrik"
}
}
ฉันพยายามทำงานกับนามแฝงแบบเรียกซ้ำ แต่ไม่สามารถทำได้
alias Person = Hash(String, Person) | Hash(String, String)
อินพุตอาจมาจากอาร์เรย์ของ String เช่น
["John|Lara|Niko", "John|Kobe", "Jess|Alex"]
["Doe", "Patrik"]
(ฉันสามารถจัดการกับลูปได้ปัญหาของฉันคือการเพิ่มลงใน Hash เนื่องจากไม่ทราบขนาด)
ฉันเจอการสนทนานี้ https://forum.crystal-lang.org/t/how-do-i-create-a-nested-hash-type/885 แต่น่าเสียดายที่ฉันไม่สามารถบรรลุสิ่งที่ฉันต้องการและยังคงใช้วิธีการของ Hash (หรือ JSON) (ซึ่งจำเป็น)
คำตอบ
ฉันไม่สามารถอธิบายได้ว่าคุณมาถึงผลลัพธ์ตัวอย่างของคุณจากอินพุตตัวอย่างของคุณอย่างไรดังนั้นฉันจะใช้การตั้งค่าอื่นสมมติว่าเรามีรูปแบบไฟล์การกำหนดค่าที่เรียบง่ายซึ่งมีการจัดโครงสร้างคีย์และจัดกลุ่มตามลำดับจุดและ ค่าทั้งหมดเป็นสตริงเสมอ
app.name = test
app.mail.enable = true
app.mail.host = mail.local
server.host = localhost
server.port = 3000
log_level = debug
เราสามารถแยกวิเคราะห์เป็นแบบวนซ้ำได้Hashดังนี้:
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
ผลลัพธ์จะเป็น:
config # => {"app" =>
{"name" => "test", "mail" => {"enable" => "true", "host" => "mail.local"}},
"server" => {"host" => "localhost", "port" => "3000"},
"log_level" => "debug"}
หรืออีกวิธีหนึ่งที่เราสามารถแยกวิเคราะห์เป็นโครงสร้างแบบเรียกซ้ำได้:
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
ผลลัพธ์จะเป็น:
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"})
โครงสร้างแบบเรียกซ้ำในปัจจุบันมีข้อบกพร่องน้อยกว่าเล็กน้อยนำเสนอสถานที่ที่ดีสำหรับวิธีการที่กำหนดเองบนวัตถุโดเมนที่แยกวิเคราะห์ของคุณและโดยทั่วไปจะมีอนาคตที่แน่นอนมากกว่านามแฝงแบบเรียกซ้ำซึ่งบางครั้งก็มีข้อบกพร่องเล็กน้อย
ตัวอย่างเต็มใน carc.in: https://carc.in/#/r/9mxr