假设我有
A = Struct.new(:name, :languages, :index)
其中
:sections
应该是此类的项目数组:
B = Struct.new(:language, :text)
所以,像这样的 JSON:
{
"name": "Greetings",
"languages": [
{
"title": "English",
"text": "Good day"
},
{
"title": "French",
"text": "Bonjour"
}
],
"index": {
"Good": "English",
"day": "English",
"Bonjour": "French"
}
}
应该按预期解析。
我还想知道是否存在无效密钥,例如如果
"text":
附近有 "name":
条目。我知道我可以做到
JSON.parse(str, {object_class: A})
,但这会尝试将"sections"
部分解析为A
并失败。Hash
来代替 "index":
,而且会失败得更惨。A
对象或 B
子对象及其声明的类函数。
我可能可以使用
JSON.parse(str, {object_class: Struct}
或 OpenStruct
,但这会很乐意接受 JSON 哈希中的任何错误键。无论是在解析过程中还是在单独的步骤中,此时绝对有必要检查无效键。
我该怎么办?
全面披露:我刚刚学习 Ruby,所以如果有一些简单的方法来实现解析和检查键有效性(值有效性也可能成为一个问题,但现在还不是),我不会感到惊讶。
没有答案,只有框架挑战,所以这就是我最后做的事情: 首先解析,然后将哈希值输入到构造函数中进行验证。
类似这样的:
# Read a data field from the JSON and
def yank_json_field(json, field)
result = json[field]
json.delete(field)
result
end
class JsonPopulatedClass
# json_data is from something like JSON.parse(File.read(filepath))
# Making the constructor accept a hash so it's testable.
# This code assumes the data is from JSON, but actually it could be from
# anywhere. However, it's destructive on the input hashes, so they should
# be temporary, and I fail to imagine what use cases other than JSON would
# give a temporary hash.
def initialize(json_data)
# For each expected field: (1) get the data, (2) do any validation.
self.titles = yank_json_field(json_data, 'titles')
# Do any default values needed.
self.titles = {} if self.titles.nil?
# This one needs to be a hash.
raise "titles must be a hash but was #{self.titles.class}" unless self.titles.is_a?(Hash)
# Untested: subobject
self.subobject = Subobject.new(yank_json_field(json_data, 'subobject'))
# Should be the same kind of constructor that raises with invalid data.
# Here's a primitive field.
self.website_root = yank_json_field(json_data, 'website_root')
# Default value; it's a truthy value and nil would be fine, but whatever.
self.website_root = false if self.website_root.nil?
# Unknown data probably means there's a typo in a field name,
# so it should be reported.
raise("Extraneous JSON data #{json_data}") unless json_data.empty?
end
end
对于我来说,Is“过于手工编码”,但它的优点是可以根据需要进行各种字段映射和奇怪的验证。
这也是一个缺点,因为任何维护者都必须查看对象和子对象的所有构造函数以捕获可能发生的任何特殊情况。
干扰:
YAML?当然,它允许对类型进行注释。但是,输入数据是手动编辑的,并且类型信息不应包含在输入数据中。
单独验证和解析?这是一个有点模糊的主题,您认为无效的字段名称是语法错误还是内容错误?
上面的配方让
JSON.parse
捕获 JSON 结构的错误,但检查字段名称;本质上,这只是一个问题,即对每一类错误执行哪种类型的异常或错误处理,这是应用程序的域(我的域恰好是手工编辑的 JSON,其中的差异完全无关,因为无论错误是什么,编辑器需要打开 JSON 文件并纠正错误)。