简体   繁体   中英

remove tpe from json after serialization via scala-pickling

Is there an easy way to serialize to json without "tpe" field inside an object? I need to serialize case classes to json structures and then, send them over the wire (They won't been deserialized in future). I have a specific api, so.. I don't need additional fields.

For class Person illustrated below:

case class Person(Name: String, Age: int, CandyLover: Boolean)

I'd like to see following:

{
  "Name": "Paul",
  "Age": 23,
  "CandyLover": true
}

I would suggest you to take a look at spray-json or argonaut. Or play-json if you are already using Play.

Scala pickling is not really a json library, it was not created to generate JSON for public API, it's not flexible, you can't define JSON protocol first and then provide pickling serialization to match protocol. Pickling is for computer-to-computer serialization where no one is really care about bytes going between apps.

Probably the simplest way to do it is to pass somehow the Hint into JSONPickleBuilder . Quick way to do it is to override builders in JSONPickleFormat by calling hintStaticallyElidedType() method from PickleTools .

Here is kind of code snippet to demonstrate it:

import org.specs2.matcher.JsonMatchers
import org.specs2.mutable.Specification
import org.specs2.specification.Scope

import scala.pickling.Defaults._
import scala.pickling.json.{JSONPickleBuilder, JSONPickleFormat, JsonFormats}
import scala.pickling.{Output, PBuilder, PickleTools, StringOutput}

class PersonJsonFormatsTest extends Specification with JsonMatchers {
  trait Context extends Scope with PersonJsonFormats

  trait PersonJsonFormats extends JsonFormats {
    override implicit val pickleFormat: JSONPickleFormat = new PersonJSONPickleFormat
  }

  class PersonJSONPickleFormat extends JSONPickleFormat {
    override def createBuilder() = new JSONPickleBuilder(this, new StringOutput) with PickleTools {
      hintStaticallyElidedType()
    }
    override def createBuilder(out: Output[String]): PBuilder = new JSONPickleBuilder(this, out) with PickleTools {
      hintStaticallyElidedType()
    }
  }

  "Pickle" should {
    "Serialize Person without $type field in resulting JSON" in new Context {
      case class Person(Name: String, Age: Int, CandyLover: Boolean)

      val pickledPersonObject = Person("Paul", 23, CandyLover = true).pickle
      println(pickledPersonObject.value)
      pickledPersonObject.value must not */("$type" → ".*".r)
    }
  }
}

The output of println(pickledPersonObject.value) will be as you need:

{
  "Name": "Paul",
  "Age": 23,
  "CandyLover": true
}

This way you will stay aligned with further pickle updates.

PS If someone knows more elegant and convenient way to reach the same behaviour - please let us know :-)

For scala-pickling 0.10.x:

import scala.pickling._
import scala.pickling.json.{JSONPickle, JSONPickleBuilder, JSONPickleFormat, JsonFormats}
import scala.pickling.pickler.AllPicklers

object serialization extends JsonFormats with Ops with AllPicklers {
  override implicit val pickleFormat: JSONPickleFormat = new JSONPickleFormat {

    private def setHints(h: Hintable): Unit = {
      h.hintStaticallyElidedType()
      h.hintDynamicallyElidedType()
    }

    override def createBuilder(): JSONPickleBuilder = {
      val builder = super.createBuilder()
      setHints(builder)
      builder
    }

    override def createBuilder(out: Output[String]): PBuilder = {
      val builder = super.createBuilder(out)
      setHints(builder)
      builder
    }

    override def createReader(pickle: JSONPickle): PReader = {
      val reader = super.createReader(pickle)
      setHints(reader)
      reader
    }
  }
}

object SerializationTest extends App {
  import serialization._

  case class Person(firstName: String, lastName: String)

  val pickle: JSONPickle = Person("Evelyn", "Patterson").pickle
  val jsonString: String = pickle.value // {"firstName": "Evelyn","lastName": "Patterson"}

  val person: Person = jsonString.unpickle[Person]
}

For scala-pickling 0.11.x:

import scala.pickling._
import scala.pickling.json.{JSONPickle, JSONPickleBuilder, JSONPickleFormat}
import scala.pickling.pickler.AllPicklers

object serialization extends AllPicklers {

  private final class CustomJSONPickleFormat(tag: FastTypeTag[_]) extends JSONPickleFormat {

    private def setHints(h: Hintable) {
      h.hintElidedType(tag)
    }

    override def createBuilder(): JSONPickleBuilder = {
      val b = super.createBuilder()
      setHints(b)
      b
    }
    override def createBuilder(out: Output[String]): PBuilder = {
      val b = super.createBuilder(out)
      setHints(b)
      b
    }
    override def createReader(pickle: JSONPickle): PReader = {
      val b = super.createReader(pickle)
      setHints(b)
      b
    }
  }

  implicit val staticOnly = static.StaticOnly // for compile time serialization methods generation

  implicit final class EncodeDecodeOps[T](picklee: T) {
    def encode(implicit pickler: Pickler[T]): String = {
      val pickleFormat = new CustomJSONPickleFormat(pickler.tag)
      functions.pickle(picklee)(pickleFormat, pickler).value
    }

    def decode[A](implicit c: T => String, unpickler: Unpickler[A]): A = {
      val pickleFormat = new CustomJSONPickleFormat(unpickler.tag)
      functions.unpickle[A](json.JSONPickle(picklee))(unpickler, pickleFormat)
    }
  }
}

case class Person(firstName: String, lastName: String) {
   @transient var x = "test"
}

object SerializationTest extends App {
  import serialization._

  val jsonString = Person("Lisa", "Daniels").encode

  println(jsonString)

  val person = jsonString.decode[Person]

  println(person)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM