Ruby读取YAML游戏存档无法访问Hangman实例变量值的问题
问题产生原因
报错的核心原因有两个:
- 存档时你使用
[] << self将Hangman实例包裹在数组中再序列化,因此YAML文件加载后的顶层结构是长度为1的数组,数组索引0位置的元素才是反序列化得到的Hangman对象,你直接对顶层数组调用键值访问方法必然出错。 - YAML中的
!ruby/object:Hangman是Ruby内置对象序列化的类型标记,加载该标记的内容时,YAML解析器会直接还原出Hangman类的实例对象,而非普通的键值对哈希结构,因此无法使用dig、[]等哈希专属的键访问方法读取内部实例变量。
你调试时打印的输出已经验证了这点:yaml.class返回Array,yaml[0]才是带所有实例变量的Hangman实例。
修复方案
你可以选择以下任意一种方案修复:
方案1:仅修改读档逻辑,兼容现有存档格式
不需要重新生成存档,调整读档方法先取出数组内的Hangman实例,再读取实例变量赋值即可。注意Ruby 2.7+版本加载自定义类序列化的YAML时,需要显式声明允许加载的类,避免触发安全拦截。
# 先在Hangman类中开放实例变量访问权限(如果没加的话) attr_accessor :secret_word, :secret_word_immutable, :secret_word_display, :guesses, :end_game, :wrong_letters_guessed, :right_letters_guessed def load_game # 加载时声明允许反序列化Hangman类 saved_array = YAML.load_file('saved_game.yml', permitted_classes: [Hangman]) saved_game = saved_array[0] # 取出数组中存储的Hangman实例 @secret_word = saved_game.secret_word @secret_word_immutable = saved_game.secret_word_immutable @secret_word_display = saved_game.secret_word_display @guesses = saved_game.guesses @end_game = saved_game.end_game @wrong_letters_guessed = saved_game.wrong_letters_guessed @right_letters_guessed = saved_game.right_letters_guessed puts "Saved game loaded!" end
如果你不想添加attr_accessor,也可以直接用instance_variable_get方法读取实例变量,例如saved_game.instance_variable_get(:@secret_word)。
方案2:优化存档逻辑,去掉多余的数组包裹
从根源简化结构,修改存档方法直接序列化Hangman实例本身,不再套一层空数组:
def save_game File.open('saved_game.yml', 'w') { |f| YAML.dump(self, f) } puts 'Game Saved!' end
对应读档方法不需要再取数组索引,直接读取加载后的对象即可:
def load_game saved_game = YAML.load_file('saved_game.yml', permitted_classes: [Hangman]) @secret_word = saved_game.instance_variable_get(:@secret_word) @secret_word_immutable = saved_game.instance_variable_get(:@secret_word_immutable) @secret_word_display = saved_game.instance_variable_get(:@secret_word_display) @guesses = saved_game.instance_variable_get(:@guesses) @end_game = saved_game.instance_variable_get(:@end_game) @wrong_letters_guessed = saved_game.instance_variable_get(:@wrong_letters_guessed) @right_letters_guessed = saved_game.instance_variable_get(:@right_letters_guessed) puts "Saved game loaded!" end
注意:如果使用Ruby 3.0及以上版本,省略
permitted_classes参数会抛出Psych::DisallowedClass异常,这是YAML解析器的默认安全机制,防止恶意构造的YAML文件实例化危险类。
内容的提问来源于stack exchange,提问作者mikerachman1
相关产品推荐
相关产品推荐

