【问题标题】:Pattern match Jackson JSON in ScalaScala中的模式匹配Jackson JSON
【发布时间】:2012-12-01 20:22:48
【问题描述】:

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

如果这不可能,我将如何添加此功能?我正在考虑从 JsonNode/ObjectNode 到 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")
}

【问题讨论】:

  • 除了在这里提问之外,您可能还想在 Jackson 邮件列表上提问(Scala 模块还没有单独的邮件,只有普通的 'user@jackson.codehaus.org')。
  • 感谢您的想法,我也将问题发布到 Jackson 邮件列表。

标签: scala pattern-matching jackson


【解决方案1】:

您是否尝试过使用案例类反序列化? 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
}

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

如果假设键的顺序,您可以想出一个类似于:::: 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

你也可以写Key1Key2Key3。这可能会也可能不会很好地扩展。

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)

【讨论】:

  • 我不确定如何使用案例类反序列化来实现我想要的。你能详细说明一下吗?
  • MyClass 到底是什么?它是代表域对象/请求还是只是您要创建的一些通用提取器。 json 可以生成您尝试提取的任意一组键吗?如果是这样,CaseClassDeserializerModule 将无济于事。大多数人不需要这样做,因为他们试图用一些已知的键集反序列化 json 字符串。如果是这种情况,则密钥不需要是提取的一部分。您可以创建案例类来表示您可以反序列化到的每种可能的类型。
  • 最好的解决方案是让 MyClass 成为通用提取器。但是,如果这不可能,自定义域对象也足够了。但是,我需要在运行时匹配对象,因为我不知道编译时 json 中的内容。基于此,您的第一个代码示例可能就是我正在寻找的。但是,如果可能的话,我想知道是否可以使用案例类反序列化的解决方案,我不知道它需要序列化到哪个对象。类似于您的第一个示例。
  • 我知道的不够多,我会确保您仔细考虑您是否真的需要那种通用的东西。从您的示例中,您似乎期望 json 包含 "key1"/"value1" 和一些 "key2",其值为 uknown 值 y。你怎么知道期待这些钥匙?这是否代表某种已知类型?如果"key1" 包含除"value1" 之外的值怎么办?我不认为CaseClassDeserializerModule 会在这里为您提供帮助。
猜你喜欢
  • 1970-01-01
  • 2012-10-31
  • 2016-05-17
  • 2021-12-17
  • 2016-03-29
  • 2019-05-26
  • 1970-01-01
  • 1970-01-01
  • 2011-08-23
相关资源
最近更新 更多