Crystal-lang: JSON récursif ou Hash
J'essaye de créer un JSON ou un Hash qui peut avoir N profondeur. Exemple: X personnes avec des noms uniques peuvent avoir Y enfants et ces enfants peuvent avoir Z enfants (et cela continue jusqu'à N générations). Je veux créer un Hash (ou JSON) qui ressemblerait à ceci:
{
"John" => {
"Lara" => {
"Niko" => "Doe"
},
"Kobe" => "Doe"
},
"Jess" => {
"Alex" => "Patrik"
}
}
J'ai essayé de travailler avec des alias récursifs mais je n'y suis pas parvenu.
alias Person = Hash(String, Person) | Hash(String, String)
L'entrée peut provenir de tableaux de String comme
["John|Lara|Niko", "John|Kobe", "Jess|Alex"]
["Doe", "Patrik"]
(Je peux gérer les boucles. Mon problème est de les ajouter au hachage car leur taille est inconnue.)
Je suis tombé sur cette discussion https://forum.crystal-lang.org/t/how-do-i-create-a-nested-hash-type/885 mais malheureusement je ne peux pas réaliser ce que je veux et aussi garder les méthodes de Hash (ou JSON) (qui sont nécessaires).
Réponses
Je ne pouvais pas tout à fait comprendre comment vous êtes arrivé à votre résultat d'exemple à partir de votre entrée d'exemple, donc je vais utiliser une configuration différente: supposons que nous ayons un format de fichier de configuration simple où les clés sont structurées et regroupées dans une séquence en pointillés toutes les valeurs sont toujours des chaînes.
app.name = test
app.mail.enable = true
app.mail.host = mail.local
server.host = localhost
server.port = 3000
log_level = debug
Nous pouvons l'analyser en un récursif Hashcomme ceci:
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
La sortie sera:
config # => {"app" =>
{"name" => "test", "mail" => {"enable" => "true", "host" => "mail.local"}},
"server" => {"host" => "localhost", "port" => "3000"},
"log_level" => "debug"}
Alternativement, nous pouvons l'analyser en une structure récursive:
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
La sortie sera alors:
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"})
Les structures récursives sont actuellement un peu moins boguées, offrent un bon emplacement pour les méthodes personnalisées sur vos objets de domaine analysés et ont généralement un avenir plus certain que les alias récursifs, qui sont parfois un peu bogués.
Exemple complet sur carc.in: https://carc.in/#/r/9mxr