Json的案例类:关于如何避免重复模式匹配而不使用外部Json库的设计模式
我正在学习Scala案例类和设计模式。为此,我创建了一个示例,我认为在处理Json类型数据时,下面的示例很可能是一个场景。我知道有一些库可以做这些事情,但我正在手动学习Scala解决问题的方法,因为使用库不会帮助我学习 我想做的主要设计改进是抽象公共代码 假设我的代码库由许多case类组成,其中每个case类都是可序列化的:Json的案例类:关于如何避免重复模式匹配而不使用外部Json库的设计模式,json,scala,pattern-matching,abstraction,case-class,Json,Scala,Pattern Matching,Abstraction,Case Class,我正在学习Scala案例类和设计模式。为此,我创建了一个示例,我认为在处理Json类型数据时,下面的示例很可能是一个场景。我知道有一些库可以做这些事情,但我正在手动学习Scala解决问题的方法,因为使用库不会帮助我学习 我想做的主要设计改进是抽象公共代码 假设我的代码库由许多case类组成,其中每个case类都是可序列化的: trait Base { def serialize(): String } trait Animal extends Base trait Mamm
trait Base {
def serialize(): String
}
trait Animal extends Base
trait Mammal extends Animal
trait Reptile extends Animal
case class Lizard(name: String, tail: Boolean) extends Reptile {
def serialize(): String = s"""{name: $name, tail: $tail}"""
}
case class Cat(name: String, age: Int) extends Mammal {
def serialize(): String = s"""{name: $name, age: $age}"""
}
case class Fish(species: String) extends Animal {
def serialize(): String = s"""{species: $species}"""
}
case class Pets(group_name: String, cat: Option[Cat] = None, lizard: Option[Lizard] = None, fish: Fish) extends Base {
def serialize(): String = {
// cat and lizard serialize in a similar fashion
val cat_object = cat match {
case Some(c) => s"""cats: ${c.serialize()}"""
case _ => ""
}
val lizard_object = lizard match {
case Some(d) => s"""lizards: ${d.serialize()}"""
case _ => ""
}
// fish serializes in a different way as it is not an option
val fish_object = s"""fish: ${fish.serialize()}"""
s"""{$lizard_object, $cat_object, $fish_object}"""
}
}
val bob = Cat("Bob", 42)
val jill = Lizard("Jill", true)
val pets = Pets("my group", Some(bob), Some(jill), Fish("goldfish")).serialize()
println(pets)
}
现在这里有一个重复的模式:
一般来说,这是一件棘手的事情。此代码并不总是使用输出中的所有字段(例如
group\u name
),并且字段的名称并不总是与字符串中的名称匹配(例如cat
vscats
)
但是,有一些Scala技巧可以使现有代码更干净:
trait Base {
def serial: String
}
trait Animal extends Base
trait Mammal extends Animal
trait Reptile extends Animal
case class Lizard(name: String, tail: Boolean) extends Reptile {
val serial: String = s"""{name: $name, tail: $tail}"""
}
case class Cat(name: String, age: Int) extends Mammal {
val serial: String = s"""{name: $name, age: $age}"""
}
case class Fish(species: String) extends Animal {
val serial: String = s"""{species: $species}"""
}
case class Pets(group_name: String, cat: Option[Cat] = None, lizard: Option[Lizard] = None, fish: Fish) extends Base {
val serial: String = {
// cat and lizard serialize in a similar fashion
val cat_object = cat.map("cats: " + _.serial)
val lizard_object = lizard.map("lizards: " + _.serial)
// fish serializes in a different way as it is not an option
val fish_object = Some(s"""fish: ${fish.serial}""")
List(lizard_object, cat_object, fish_object).flatten.mkString("{", ", ", "}")
}
}
val bob = Cat("Bob", 42)
val jill = Lizard("Jill", true)
val pets = Pets("my group", Some(bob), Some(jill), Fish("goldfish")).serial
println(pets)
由于case类
是不可变的,序列化的值不会更改,因此将其设置为名为serial
的属性更有意义
选项
值最好在选项
中使用映射进行处理,然后在最后提取。在本例中,我使用了plant
将列表[选项[String]]
转换为列表[String]
如果其中一个选项为空,mkString
方法是格式化列表和避免输出中出现,
的好方法。这里有一种通用方法,可以利用案例类是产品的事实,为案例类创建有限的序列化方法
def basicJson(a: Any): String = a match {
case Some(x) => basicJson(x)
case None => ""
case p: Product =>
(p.productElementNames zip p.productIterator.map(basicJson _))
.map(t => s"${t._1}: ${t._2}")
.mkString("{", ", ", "}")
case _ => a.toString
}
如果要在不使用组名的情况下序列化Pets
,可以在Pets
中定义一个val
,手动序列化除group\u name
之外的所有字段:
val toJson = s"{cat: ${basicJson(cat)}, lizard: ${basicJson(lizard)}, fish: ${basicJson(fish)}}"
此代码的输出
val bob = Cat("Bob", 42)
val jill = Lizard("Jill", true)
val pets = Pets("my group", Some(bob), Some(jill), Fish("goldfish"))
println(pets.toJson)
这是:
{cat: {name: Bob, age: 42}, lizard: {name: Jill, tail: true}, fish: {species: goldfish}}
在斯卡斯蒂:
学习Scala时,这不是一个很好的起点,因为您正在将源代码符号名称放入数据值中。这涉及到使用在Scala(或者任何语言)中都很笨拙的反射。虽然这是在处理JSON时使用的,但正如您所说,现有的库可以完成这项工作。虽然这两种语言本身都不是很难,但在没有掌握语言其余部分的情况下学习它是一种受虐狂。谢谢,我将推迟学习运行时/编译时反射。我以前从未遇到过这个概念;博士这类问题的答案是a。请参阅。我知道你的问题说你不想使用它,但我还是推荐它酷,我喜欢映射而不是选项。我说这是利用选项monad属性,对吗?当单词“monad”出现时,我总是很紧张。我认为这是Scala库在处理像Option
、Try
和Future
这样的集合对象时一致性的一个例子。map
、flatMap
和foreach
等方法在这些类型上以“预期方式”运行,就像在正常收集(如List
)中一样。但是我相信map
(或者更确切地说flatMap
)是monad的特征方法之一。