使用Jackson来(De) - 将Scala案例类序列化

时间:2015-02-02 03:11:28

标签: java json scala serialization jackson

我使用Jackson测试了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 } """
}

当我运行上面的main Java类时,抛出了一个异常:

[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) )

我如何(de)-serial化Scala案例类?

6 个答案:

答案 0 :(得分:16)

杰克逊希望你的类成为一个JavaBean,这意味着它希望这个类对每个属性都有一个getX()和/或setX()。

选项1

您可以使用注释BeanProperty在Scala中创建JavaBean类。

实施例

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

在这种情况下,val将仅表示定义了一个getter。如果需要用于反序列化的setter,则将属性定义为var。

选项2

虽然选项1可行,但如果你真的想使用Jackson,那么有一些包装器可以处理像FasterXML's scala module这样的Scala类,这可能是一种更好的方法。我没有使用它,因为我刚刚使用内置的Json库来玩。

答案 1 :(得分:13)

找到一个适用于jackson和scala案例类的解决方案。

我使用了一个scala模块用于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"
)

我必须使用@JsonProperty在我的case类中注释字段。

这就是我的案例类:

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

这就是我反序列化的方式:

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

序列化更容易:

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

想澄清我正在使用

com.fasterxml.jackson.databind.ObjectMapper

在问题中,似乎他正在使用

org.codehaus.jackson.map.ObjectMapper 

不能使用ScalaObjectMapper。

答案 2 :(得分:2)

根据Priyank Desai的回答,我创建了一个将json string转换为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)
}

用法:

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

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

答案 3 :(得分:0)

  • 我创建了一个通用函数来转换JSON String to Case Class/ObjectCase Class/Object to JSON String

  • 请找到我使用泛型here提供的有效且详细的答案。

答案 4 :(得分:0)

使用 upickle 反序列化对象比使用 Jackson 容易得多。举个例子:

var toc = (TableOfEntries)document.GetChildElements(true, ElementType.TableOfEntries).First();
toc.Update();
document.GetPaginator(new PaginatorOptions() { UpdateFields = true });

Jackson 解决方案更加冗长。请参阅此 post 以了解有关此方法的更多信息。

答案 5 :(得分:0)

如果您使用的是 Spring MVC,请使用如下配置:

import java.util.List

@Configuration
@EnableWebMvc
class WebConfig extends WebMvcConfigurer {
  
  override def configureMessageConverters(converters: List[HttpMessageConverter[_]]): Unit = {
    val conv = new MappingJackson2HttpMessageConverter();
    val mapper = JsonMapper.builder()
      .addModule(DefaultScalaModule)
      .build();
    conv.setObjectMapper(mapper);
    converters.add(conv);
  }
}

然后序列化/反序列化普通案例类和集合类型将按预期工作。