2

我想知道是否可以在 Scala 中对Jackson JSON 对象进行模式匹配。我们目前在一个项目中大量使用jackson-module-scala,并且能够从 Json ObjectNode/JsonNode 对象的模式匹配中受益。

如果这不可能,我将如何添加此功能?我在考虑从 JsonNode/ObjectNode 到 MyClass 的隐式转换MyClass,其中 MyClass 有unapply方法,做 JsonNode.toString 和正则表达式匹配。如果我的逻辑是正确的,那么我可以对 JsonNode 对象进行模式匹配。当然,可能有更好的方法我不知道,或者由于我还不知道的原因,这个方法可能不起作用。为了说明我的情况,我希望能够执行以下操作:

val mapper = new ObjectMapper()
mapper.registerModule(DefaultScalaModule)

val json = mapper.createObjectNode()
                .put("key1","value1")
                .put("key2","value2")
                .put("key3","value3")

json match {
  case MyClass("key1", "value1", "key2", y) => println("Found key1 with value1, where key2 is " + y)
  case MyClass("key1", x) => println("Key1 value is " + x)
  ...
  _ => println("No match found")
}
4

1 回答 1

3

您是否尝试过使用案例类反序列化? https://github.com/FasterXML/jackson-module-scala/blob/master/src/test/scala/com/fasterxml/jackson/module/scala/deser/CaseClassDeserializerTest.scala

如果这不起作用,我认为您最好创建提取器来表示您的域对象。下面的代码假设一个 scala Map,但它应该给你一个想法。不需要隐式转换。

case class DomainObjectA(v1: String, v2: String)
object DomainObjectAExtractor {
  def unapply(m: Map[String, String]) = for {
     v1 <- m.get("key1")
     v2 <- m.get("key2")
  } yield DomainObjectA(v1, v2)
}

case class DomainObjectB(v3, v4, v5)
object DomainObjectBExtractor {
  def unapply(m: Map[String, String]) = for {
     v3 <- m.get("key3")
     v4 <- m.get("key4")
     v5 <- m.get("key5")
  } yield DomainObjectB(v3, v4, v5)
}

json match {
  case DomainObjectAExtractor(a@DomainObjectA(_, _)) => a
  case DomainObjectBExtractor(b@DomainObjectB(_, _, _)) => b
}

但是,如果您坚持尝试匹配键/值对,则可能有一些方法可以完成您可以接受的事情。无法将输入从 传递到 unapply 函数case,如果我理解您想要正确执行的操作,我认为这将是必需的。可以使用即将正式发布的 scala 2.10 中的实验性宏来执行此操作。我和他们玩得还不够多,不知道这是否可能。

如果假设键的顺序,您可以想出一个::类似于::for list 的 unapply 运算符。这可以K, V以这个已知的顺序提取对。就我个人而言,这对我的口味来说太脆弱了。

val json = Map(("key1" -> "one"), ("key2" -> "two"))

object -> {
  def unapply[A, B](ab: (A, B)) = Some(ab)
}

object :: {
  def unapply[K, V](m: Map[K, V]): Option[((K, V), Map[K, V])] = 
    m.headOption.map(_ -> m.tail)
}

scala> json match {
     |   case ("key1" -> "one") :: ("key2" -> value2) :: _ => value2
     | }
res0: java.lang.String = two

您将无法以错误的顺序提取密钥

scala> json match {
     |   case ("key2" -> value2) :: _ => value2
     |   case _ => "match fail"
     | }
res2: java.lang.String = match fail

你也可以写Key1, Key2, Key3。这可能会或可能不会很好地扩展。

object && {
  def unapply[A](a: A) = Some((a, a))
}

object Key2 {
  def unapply[V](m: Map[String, V]) = m.get("key2")
}

object Key1 {
  def unapply[V](m: Map[String, V]) = m.get("key1")
}

scala> json match {
     |   case Key2(value2) && Key1(value1) => (value2, value1)
     | }
res5: (java.lang.String, java.lang.String) = (two,one)
于 2012-12-02T02:07:10.137 回答