MappingException: неизвестная ошибка при использовании json4s

Я пытаюсь разобрать простой Json с помощью json4s и обнаружил, что он будет работать в основном классе моей программы, но по какой-то причине не в модульном тесте. Вот минимальный пример:

build.sbt:

ThisBuild / scalaVersion     := "2.13.2"

lazy val validatorbroken = (project in file("."))
  .settings(
    name := "validatorbroken",
  )

libraryDependencies ++= Seq(
  "org.json4s" %% "json4s-jackson" % "3.7.0-M4",
  "org.scalatest" %% "scalatest" % "3.1.2" % "test",
)

файл, содержащий определение класса case и функцию:

package validatorbroken

import org.json4s._
import org.json4s.jackson.JsonMethods._

case class EasyInput(file: String, sheet: String)

object ProcessInput {
  import Main.formats
  def captureEasyInput(s: String): EasyInput = {
    println(s"STRING JSON $s")
    val rawJson = parse(s)
    println(s"PARSED JSON $rawJson")
    rawJson.extract[EasyInput]
  }
}

Основной файл, который отлично работает:

package validatorbroken

import org.json4s._
import scala.io.Source

object Main extends App {
  implicit val formats = DefaultFormats
  val easyInputJson: String = Source.fromResource("easy_input.json").mkString
  val capturedJson = ProcessInput.captureEasyInput(easyInputJson)
  println(s"CAPTURED $capturedJson")
}

и модульный тест, который не:

package validatorbroken 

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import scala.io.Source

class UnitTests extends AnyFlatSpec with Matchers {
  implicit val formats = Main.formats
  import ProcessInput._

  "captureInput Function" should "obtain an instance of Input" in {
    val easyInputJson: String = Source.fromResource("easy_input.json").mkString
    val capturedJson = ProcessInput.captureEasyInput(easyInputJson)
    println(s"CAPTURED $capturedJson")
  }
}

Я знаю, что есть некоторые проблемы с тем, что ваши классы case не находятся на верхнем уровне пакета, но, насколько я могу видеть, я не наступал на эту конкретную шахту. Может ли кто-нибудь протянуть руку? Рискуя показаться идиотом, если окажется, что это простое решение, это уже стоило мне нескольких часов работы.

Моя трассировка стека выглядит так:

[info] - should obtain an instance of Input *** FAILED ***
[info]   org.json4s.package$MappingException: unknown error
[info]   at org.json4s.Extraction$.extract(Extraction.scala:46)
[info]   at org.json4s.ExtractableJsonAstNode.extract(ExtractableJsonAstNode.scala:21)
[info]   at validatorbroken.ProcessInput$.captureEasyInput(InputToItems.scala:19)
[info]   at validatorbroken.UnitTests.$anonfun$new$1(Basic.scala:13)
[info]   at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
[info]   at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]   at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
[info]   at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:22)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:20)
[info]   ...
[info]   Cause: java.lang.NullPointerException:
[info]   at org.json4s.Formats$.customDeserializer(Formats.scala:54)
[info]   at org.json4s.Extraction$.customOrElse(Extraction.scala:662)
[info]   at org.json4s.Extraction$.extract(Extraction.scala:410)
[info]   at org.json4s.Extraction$.extract(Extraction.scala:42)
[info]   at org.json4s.ExtractableJsonAstNode.extract(ExtractableJsonAstNode.scala:21)
[info]   at validatorbroken.ProcessInput$.captureEasyInput(InputToItems.scala:19)
[info]   at validatorbroken.UnitTests.$anonfun$new$1(Basic.scala:13)
[info]   at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
[info]   at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]   at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)

Заранее благодарим за любую помощь, а также после.


person Chris J Harris    schedule 14.06.2020    source источник
comment
ВНИМАНИЕ: Json4s уязвим. под DoS / DoW-атаками!   -  person Andriy Plokhotnyuk    schedule 14.06.2020


Ответы (1)


...
Cause: java.lang.NullPointerException
...

Это хороший признак проблемы порядка инициализации.

Вы используете import Main.formats в ProcessInput и ProcessInput в Main. Я не могу точно понять, почему это работает в Main, но не в модульном тесте, но я предлагаю переместить определение formats в ProcessInput или сделать его неявным аргументом для captureEasyInput.

person Koterpillar    schedule 14.06.2020
comment
Вот и все. Это исправляет перенос определения форматов в ProcessInput. Спасибо, у меня всегда были такие проблемы в Python, но у меня была эта (очевидно наивная) идея, что на компилируемом языке это будет меньше проблем. Приятно иметь здесь какое-то закрытие и знать, что виноват я, а не библиотека. - person Chris J Harris; 14.06.2020