2015-02-02 10 views
6

Ho testato la serializzazione di una classe di case Scala utilizzando Jackson.Uso di Jackson per (De) -serializzare una classe di case Scala

DeserializeTest.java

public static void main(String[] args) throws Exception { // being lazy to catch-all 

     final ObjectMapper mapper   = new ObjectMapper(); 
     final ByteArrayOutputStream stream = new ByteArrayOutputStream(); 

     mapper.writeValue(stream, p.Foo.personInstance()); 

     System.out.println("result:" + stream.toString()); 
    } 
} 

Foo.scala

object Foo { 
    case class Person(name: String, age: Int, hobbies: Option[String]) 
    val personInstance = Person("foo", 555, Some("things")) 
    val PERSON_JSON = """ { "name": "Foo", "age": 555 } """ 
} 

Quando ho eseguito quanto sopra main della classe Java, È stata generata un'eccezione:

[error] Exception in thread "main" org.codehaus.jackson.map.JsonMappingException: 
No serializer found for class p.Foo$Person and no properties discovered 
to create BeanSerializer (to avoid exception, 
disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS)) 

Come è possibile (de) -sializzare le classi caso Scala?

+1

Ci sono diversi buoni Scala wrapper per Jackson; dovresti almeno considerare di usarne uno. Il fatto è che Play-json è quello che uso. I buoni (IMO) utilizzano la de/serializzazione basata sulla classe di caratteri, in cui si aggiungono membri all'oggetto companion della classe case per dirgli come eseguire il mapping avanti e indietro su JSON e fornire buoni idiomi per farlo. –

risposta

10

Jackson si aspetta che la tua classe sia un JavaBean, il che significa che si aspetta che la classe abbia un getX() e/o setX() per ogni proprietà.

Opzione 1

È possibile creare classi JavaBean a Scala usando l'annotazione BeanProperty.

Esempio

case class Person(
    @BeanProperty val name: String, 
    @BeanProperty val age: Int, 
    @BeanProperty val hobbies: Option[String] 
) 

In questo caso una val significherà solo un getter è definito. Se vuoi setter per la deserializzazione hai definito le proprietà come var.

Opzione 2

mentre l'opzione 1 funzionerà, se davvero si vuole utilizzare Jackson ci sono involucri che le permettono di affrontare con le classi Scala come FasterXML's scala module che potrebbe essere un approccio migliore. Non l'ho usato perché ho appena usato la libreria Json per giocare.

8

Ha trovato una soluzione che funziona con classi di jackson e scala case.

Ho usato un modulo scala per jackson - jackson-module-scala.

libraryDependencies ++= Seq(
"com.fasterxml.jackson.core" % "jackson-databind" % "2.5.3", 
"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.2.2" 
) 

Ho dovuto annotare campi nella mia classe case con @JsonProperty.

Questo è ciò che sembra la mia classe caso come:

case class Person(@JsonProperty("FName") FName: String, @JsonProperty("LName") LName: String) 

E questo è come mi deserializzare:

val objectMapper = new ObjectMapper() with ScalaObjectMapper 
objectMapper.registerModule(DefaultScalaModule) 
val str = """{"FName":"Mad", "LName": "Max"}""" 
val name:Person = objectMapper.readValue[Person](str) 

serializzazione è più facile:

val out = new ByteArrayOutputStream() 
objectMapper.writeValue(out, name) 
val json = out.toString 

teniamo a precisare che Sto usando

com.fasterxml.jackson.databind.ObjectMapper 

Nella questione, sembra che lui sta usando

org.codehaus.jackson.map.ObjectMapper 

che non funziona con ScalaObjectMapper.

+0

la tua soluzione non ha toccato l'attributo "hobbies: Option [String]" e penso che parte del suo problema fosse con la gestione di un attributo json String come Option [String]. Jackson ha bisogno di sapere come interpretare un'opzione [String] come Some [String] o None –

+4

Alcune note: 1) @JsonProperty non è necessario per le classi case esp. quando il nome della proprietà è uguale al nome del campo. 2) Dovresti provare ad usare la stessa versione di jackson-module-scala come utilizzata con jackson-core. 3) Jackson 2.0 è stato riconfezionato come com.fasterxml, ma per il resto funziona come (se non molto meglio) come Jackson 1.0. – Nate

1

in base alla risposta del Priyank Desai ho creato una funzione generica per convertire json string a case class

import com.fasterxml.jackson.annotation.JsonProperty 
import com.fasterxml.jackson.databind.ObjectMapper 
import com.fasterxml.jackson.module.scala.DefaultScalaModule 
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper 

def jsonToType[T](json:String)(implicit m: Manifest[T]) :T = { 
    val objectMapper = new ObjectMapper() with ScalaObjectMapper 
    objectMapper.registerModule(DefaultScalaModule) 
    objectMapper.readValue[T](json) 
} 

Usage:

case class Person(@JsonProperty("name") Name:String, @JsonProperty("age") Age:Int) 

val personName = jsonToType[Person](jsonString).name 
Problemi correlati