LoginSignup
5
2

More than 5 years have passed since last update.

json4s ライブラリで extract できないケース

Posted at

概要

Scala の Json ライブラリ で json4s を使っているが、とある JValue を、ある case class に
extract しようとした際、予想外に extract できないケースがあった。

こんなケース

JsonExtractTest.scala
import org.json4s.DefaultFormats
import org.json4s.JsonAST.JValue

object JsonExtractTest {

  implicit val formats = DefaultFormats

  def extract(json: JValue) = {
    json.extract[Test]    
  }

  case class Test(item: Option[List[Int]])
}

REPL で試してみる

試しに REPL にペーストして実行してみると

scala> :paste
// Entering paste mode (ctrl-D to finish)

import org.json4s.DefaultFormats
import org.json4s.JsonAST.JValue

object JsonExtractTest {

  implicit val formats = DefaultFormats

  def extract(json: JValue) = {
    json.extract[Test]    
  }

  case class Test(item: Option[List[Int]])
}

// Exiting paste mode, now interpreting.

defined object JsonExtractTest

scala> import org.json4s.native.JsonMethods._
import org.json4s.native.JsonMethods._

scala> JsonExtractTest.extract(parse("""{"item":[4,5,6]}"""))
JsonExtractTest.Test = Test(Some(List(4, 5, 6)))

予想通り。

Specs2 で確認してみる

よし、うまくいくこと確認できたぞー!と実装してSpecs2も実装

JsonExtractTestSpec.scala
import org.specs2.mutable.Specification
import org.json4s.native.JsonMethods._

class JsonExtractTestSpec extends Specification {

  "extract method" should {
    "return item" in {
      val jsonString = """{"item":[4,5,6]}"""
      val result = JsonExtractText.extract(parse(jsonString))

      result.item must beSome(List(4, 5, 6))
    }
  }
}

テスト流してみる。

> test
[info] JsonExtractTestSpec
[info]
[info] extract method should
[info] ! return item
[error]    MappingException: : Can't find constructor for ExtensionRequest  (ScalaSigReader.scala:27)
[error] org.json4s.reflect.ScalaSigReader$.readConstructor(ScalaSigReader.scala:27)
[error] org.json4s.reflect.Reflector$ClassDescriptorBuilder.ctorParamType(Reflector.scala:108)
[error] org.json4s.reflect.Reflector$ClassDescriptorBuilder$$anonfun$6.apply(Reflector.scala:98)
[error] org.json4s.reflect.Reflector$ClassDescriptorBuilder$$anonfun$6.apply(Reflector.scala:95)

...(snip)...

ほわーーーーーーーーーい!ジャパニーズピーポー!!
なんで REPL だとうまくいったのにテストコケるんだよー!!

調査

ぐぐったらソッコー出てきた。。
https://github.com/json4s/json4s/issues/125
リフレクションの関係でクラス内で定義された case class には extract できないよってことらしい。。

修正、そして結果

object ないで定義した case class Test を外に出す。
そしてテストをもっかい実行してみる。

> test
[info] JsonExtractTestSpec
[info]
[info] extract method should
[info] + return item

できた。。

まとめ

この挙動は知らなかったので勢いで投稿してしまった。
item の型を Option[List[String]] にすると case class をクラス内に定義しても extract できるという謎がありますが、いったんここまで。。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2