Crystal-lang : 재귀 JSON 또는 해시

Sep 04 2020

N 깊이를 가질 수있는 JSON 또는 해시를 만들려고합니다. 예 : 고유 한 이름을 가진 X 명은 Y 자녀를 가질 수 있고 그 아이들은 Z 자녀를 가질 수 있습니다 (그리고 N 세대까지 계속됨). 다음과 같은 해시 (또는 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"]

(나는 루프를 다룰 수 있습니다. 내 문제는 크기를 알 수 없기 때문에 해시에 추가하는 것입니다.)

이 토론을 보았습니다 https://forum.crystal-lang.org/t/how-do-i-create-a-nested-hash-type/885 그러나 불행히도 내가 원하는 것을 얻을 수 없으며 Hash의 (또는 JSON의) 메소드 (필요한)도 유지할 수 없습니다.

답변

1 JonneHaß Sep 04 2020 at 17:12

예제 입력에서 예제 결과에 어떻게 도달했는지 알 수 없었기 때문에 다른 설정을 사용하겠습니다. 키가 점으로 구분 된 시퀀스를 통해 구조화되고 그룹화되는 간단한 구성 파일 형식이 있다고 가정 해 보겠습니다. 모든 값은 항상 문자열입니다.

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