Converting (Very) Complicated JSON objects to Scal

2019-09-16 14:14发布

问题:

I have a very complicated JSON file that looks like this:

       {
            "Animals": [
                [
                    100,
                    "Mammals",
                    [
                        1,
                        "Cat",
                        50,
                        45,
                        57,
                        -1
                    ],
                    [
                        2,
                        "Dog",
                        31,
                        44,
                        18,
                        -1
                    ]
                ],
    [
                159,
                "Reptiles",
                [
                    1,
                    "Lizard",
                    11,
                    12,
                    9,
                    -1
                ]
            ]
]
    }

I am attempting to parse this structure and somehow get scala objects out of it.

Here was my attempt:

case class Facts(number: Int, subTypeOfAnimal: String, data: List[Int])

case class Animaltype(value: Int, typeOfAnimal: String, characteristics: List[Facts])

case class Animal(rows: List[Animaltype])

This, of course, fails to convert the data. It returns a JNothing. I am wondering how I can express complex JArrays within JArrays of this kind properly.

Any help would be useful

Thanks!

回答1:

You can define CustomSerializers for Facts and AnimalType.

import scala.util.Try

import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.native.JsonMethods._
import org.json4s.native.Serialization

case class Facts(number: Int, subTypeOfAnimal: String, data: List[Int])
case class AnimalType(value: Int, typeOfAnimal: String, characteristics: List[Facts])
case class Animal(Animals: List[AnimalType])

implicit val formats = Serialization.formats(NoTypeHints) + 
  new AnimalTypeSerializer + new FactsSerializer

class FactsSerializer extends CustomSerializer[Facts](format => ( {
  case JArray(JInt(nr) :: JString(subType) :: data) => 
    Facts(nr.toInt, subType, data.collect{ case JInt(i) => i.toInt})
}, { case _ => throw new RuntimeException("No serializing")}))

class AnimalTypeSerializer extends CustomSerializer[AnimalType](format => ( {
  case JArray(JInt(value) :: JString(typeAnimal) :: factsArrays) => 
    val facts = factsArrays.collect { case facts: JArray => 
      Try(facts.extract[Facts]).toOption
    }.flatten
    AnimalType(value.toInt, typeAnimal, facts)
}, { case _ => throw new RuntimeException("No serializing")}))

If you take your input json as the value json you can deserialize it with :

parse(json).extract[Animal]
// Animal = Animal(List(
//   AnimalType(100,Mammals,List(
//     Facts(1,Cat,List(50, 45, 57, -1)), Facts(2,Dog,List(31, 44, 18, -1))
//   )),
//   AnimalType(159,Reptiles,List(
//     Facts(1,Lizard,List(11, 12, 9, -1))
//   ))
// ))