Crystal-lang: Rekursives JSON oder Hash
Ich versuche, einen JSON oder einen Hash zu erstellen, der N-Tiefe haben kann. Beispiel: X Personen mit eindeutigen Namen haben möglicherweise Y Kinder und diese Kinder haben möglicherweise Z Kinder (und dauert bis zu N Generationen). Ich möchte einen Hash (oder JSON) erstellen, der folgendermaßen aussehen würde:
{
"John" => {
"Lara" => {
"Niko" => "Doe"
},
"Kobe" => "Doe"
},
"Jess" => {
"Alex" => "Patrik"
}
}
Ich habe versucht, mit rekursiven Aliasen zu arbeiten, konnte dies aber nicht erreichen.
alias Person = Hash(String, Person) | Hash(String, String)
Die Eingabe könnte von Arrays von String like stammen
["John|Lara|Niko", "John|Kobe", "Jess|Alex"]
["Doe", "Patrik"]
(Ich kann mit den Schleifen umgehen. Mein Problem besteht darin, sie dem Hash hinzuzufügen, da ihre Größe unbekannt ist.)
Ich bin auf diese Diskussion gestoßen https://forum.crystal-lang.org/t/how-do-i-create-a-nested-hash-type/885 Leider kann ich nicht erreichen, was ich will, und auch die Hash-Methoden (oder JSON-Methoden) (die benötigt werden) behalten.
Antworten
Ich konnte aus Ihrer Beispieleingabe nicht genau erkennen, wie Sie zu Ihrem Beispielergebnis gekommen sind, daher werde ich ein anderes Setup verwenden: Nehmen wir an, wir haben ein einfaches Konfigurationsdateiformat, in dem Schlüssel durch eine gepunktete Sequenz und strukturiert und gruppiert sind Alle Werte sind immer Zeichenfolgen.
app.name = test
app.mail.enable = true
app.mail.host = mail.local
server.host = localhost
server.port = 3000
log_level = debug
Wir können es Hashso rekursiv analysieren :
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
Die Ausgabe wird sein:
config # => {"app" =>
{"name" => "test", "mail" => {"enable" => "true", "host" => "mail.local"}},
"server" => {"host" => "localhost", "port" => "3000"},
"log_level" => "debug"}
Alternativ können wir es zu einer rekursiven Struktur analysieren:
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
Die Ausgabe lautet dann:
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"})
Rekursive Strukturen sind derzeit etwas weniger fehlerhaft, bieten einen guten Platz für benutzerdefinierte Methoden für Ihre analysierten Domänenobjekte und haben im Allgemeinen eine sicherere Zukunft als rekursive Aliase, die manchmal etwas fehlerhaft sind.
Vollständiges Beispiel auf carc.in: https://carc.in/#/r/9mxr