我正在考虑将一个非常简单的文本模板库移植到scala,主要是作为学习该语言的练习。该库目前同时在Python和Javascript中实现,其基本操作或多或少可以归结为以下内容(在python中):
template = CompiledTemplate('Text {spam} blah {eggs[1]}')
data = { 'spam': 1, 'eggs': [ 'first', 'second', { 'key': 'value' }, true ] }
output = template.render(data)
在Scala中,这都不是很难做到的,但是我不清楚的是如何最好地表达
data
参数的静态类型。基本上,此参数应该能够包含您在JSON中发现的各种东西:一些原语(字符串,整数, bool 值,空值)或零个或多个项目的列表,或零个或多个项目的映射。 (出于这个问题的目的,可以将 map 限制为具有字符串键,这似乎是Scala始终喜欢事物的方式。)
我最初的想法只是将
Map[string, Any]
用作顶级对象,但这对我来说似乎并不完全正确。实际上,我不想在其中添加任何类的任意对象。我只需要上面概述的元素。同时,我认为在Java中我真正能够获得的最接近的应该是Map<String, ?>
,而且我知道Scala的一位作者设计了Java的泛型。我特别好奇的一件事是,具有类似类型系统的其他功能语言如何处理此类问题。我有一种感觉,我真正想在这里提出的是一组可以进行模式匹配的案例类,但是我无法完全想象它的外观。
我在Scala中进行了编程,但是老实说,我的眼睛开始对协方差/协方差东西感到有些呆滞,我希望有人可以向我更清楚和简洁地说明这一点。
最佳答案
您发现需要某种案例类来对数据类型进行建模。在函数式语言中,这些类型的事情称为“抽象数据类型”,您可以通过谷歌搜索一点来了解有关Haskell如何使用它们的全部信息。 Scala相当于Haskell的ADT使用密封的特征和案例类。
让我们看一下Scala标准库或《 Scala编程》一书中的rewrite of the JSON parser combinator。它使用抽象数据类型JsValue
来表示JSON值,而不是使用Map [String,Any]表示JSON对象,而不是使用Any来表示任意JSON值。 JsValue
有几种子类型,表示可能的JSON值类型:JsString
,JsNumber
,JsObject
,JsArray
,JsBoolean
(JsTrue
,JsFalse
)和JsNull
。
处理这种形式的JSON数据涉及模式匹配。由于JsValue是密封的,因此如果您未处理完所有情况,编译器都会警告您。例如,toJson
的代码(该方法采用JsValue
并返回该值的String
表示形式)如下所示:
def toJson(x: JsValue): String = x match {
case JsNull => "null"
case JsBoolean(b) => b.toString
case JsString(s) => "\"" + s + "\""
case JsNumber(n) => n.toString
case JsArray(xs) => xs.map(toJson).mkString("[",", ","]")
case JsObject(m) => m.map{case (key, value) => toJson(key) + " : " + toJson(value)}.mkString("{",", ","}")
}
模式匹配不仅可以确保我们处理每种情况,还可以从其JsType中“解包”基础值。它提供了一种类型安全的方式来知道我们已经处理了每种情况。
此外,如果您在编译时知道要处理的JSON数据的结构,则可以执行n8han's extractors之类的非常酷的事情。非常强大的功能,请查看。
关于json - 我应该如何在Scala中指定类似JSON的非结构化数据的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/728871/