geotrellis使用(十九)spray-json框架介绍

Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html

目录

  1. 前言
  2. spray-json简介
  3. spray-json使用
  4. 总结

一、前言

       Json作为目前最流行的数据交换格式,具有众多优势,在Scala语言中以及当我们使用Geotrellis前后台交换数据的时候都少不了要使用Json,本文为大家介绍一款开源的Json处理框架——spray-json

二、spray-json简介

       spray-json是一款使用Scala语言编写的开源Json处理框架。GitHub地址:https://github.com/spray/spray-json。其中对其介绍如下:

spray-json is a lightweight, clean and efficient JSON implementation in Scala.

It sports the following features:

    A simple immutable model of the JSON language elements
    An efficient JSON parser
    Choice of either compact or pretty JSON-to-string printing
    Type-class based (de)serialization of custom objects (no reflection, no intrusion)
    No external dependencies

spray-json allows you to convert between

    String JSON documents
    JSON Abstract Syntax Trees (ASTs) with base type JsValue
    instances of arbitrary Scala types

       大意就是spray-json轻量、无依赖、高效,没有使用反射等。可以在JSON字符串对象、AST(JSON树)对象、Scala类型之间任意转换。

       在Scala程序中使用spray-json,只需要在build.sbt文件中添加libraryDependencies += "io.spray" %% "spray-json" % "1.3.2",然后更新sbt即可。

三、spray-json使用

       总体上使用spray-json需要先定义一个转换的协议(Protocol),该协议指定了如何在Scala对象与JOSN对象之间进行转换。spary-json也提供了一些基础类型的转换协议,在DefaultJsonProtocol类中。

3.1 基础类型转换

       首先引入spray-json以及DefaultJsonProtocol

import spray.json._
import DefaultJsonProtocol._ 

       然后可以直接进行类型转换,代码如下:

val str = """{"name":"wsf", "age":26}"""
val json: JsValue = str.toJson
val json2 = str.parseJson
println(json2.prettyPrint)
println(json2.compactPrint)
val age = 26
val json_age = age.toJson
json_age.convertTo[Int]

       使用toJson和parseJson都能将字符串或其他类型转换成JsValue,prettyPrint是将json以分行的方式优雅的输出,compactPrint直接在一行压缩输出,convertTo可以直接将json对象转为Scala对应的类型。

       DefaultJsonProtocol支持的数据类型列表如下:

  • Byte, Short, Int, Long, Float, Double, Char, Unit, Boolean
  • String, Symbol
  • BigInt, BigDecimal
  • Option, Either, Tuple1 - Tuple7
  • List, Array
  • immutable.{Map, Iterable, Seq, IndexedSeq, LinearSeq, Set, Vector}
  • collection.{Iterable, Seq, IndexedSeq, LinearSeq, Set}
  • JsValue

3.2 case class类型转换

       如果不在DefaultJsonProtocol支持的数据类型中就需要我们自己定义JsonProtocol,最简单的类型是case class,其方法如下:

case class MyInt(value: Int)
object MyIntProtocol extends DefaultJsonProtocol {
    implicit val format = jsonFormat1(MyInt) 
}

import MyIntProtocol._
val json2 = MyInt(10).toJson
println(json2)
val myInt = json2.convertTo[MyInt]
println(myInt)

       简单的说就是定义一个object类,并添加一个隐式参数,参数的值为jsonFormatX(X表示数字)函数将自定义的case类传入。

       这里需要说明的是自定义的case类有几个属性这里X就为几,即调用相应的函数。如果case类还定义了伴随的object类,那么jsonFormatX函数就需要传入MyInt.apply。并且MyIntProtocol类的定义不能放在调用位置的后面,否则会出错。

3.3 包含泛型的类型转换

       如果case类的属性中包含了泛型那么实现方法稍有不同,代码如下:

case class MyList[A](name: String, items: List[A])

object MyListProtocol extends DefaultJsonProtocol {
  implicit def myListFormat[A: JsonFormat] = jsonFormat2(MyList.apply[A])
}

import MyListProtocol._
val json3 = MyList[Int]("wsf", List(1, 2, 3)).toJson
println(json3.prettyPrint)
val myList = json3.convertTo[MyList[Int]]
println(myList)

       同样是定义一个object类,并添加一个隐式函数,不同的是传入的是MyList.apply[A],即apply加泛型,并且需要指明返回类型为[A: JsonFormat]。此处还需要说明的是在基本的case类中定义隐式变量的时候用的是implicit val,而此处用的是implicit def,个人理解是在scala中变量与函数的定义比较模糊,二者基本是等价的,但是此处返回值的类型是泛型,所以要用def。

3.4 普通class类的转换

       如果是一个普通的class类,就需要自己定义write和read方法。代码如下:

class Person(val name: String, val age: Int)

object MyPersonProtecol extends DefaultJsonProtocol {
    implicit object myPersonFormat extends RootJsonFormat[Person] {
      override def write(person: Person): JsValue = JsArray(JsString(person.name), JsNumber(person.age))

      override def read(json: JsValue): Person = json match {
        case JsArray(Vector(JsString(name), JsNumber(age))) => new Person(name, age.toInt)
        case _ => deserializationError("Person expected")
      }
    }
}

import MyPersonProtecol._
val person = new Person("wsf", 26)
val json = person.toJson
println(json.prettyPrint)

val per = json.convertTo[Person]

       此处相当于隐式format不在由jsonFormatX函数返回,而是自定义一个类并继承自RootJsonFormat。上述代码将Person实例转换成JsArray,既json数组对象,Person的各个属性按照定义的顺序存放到数组,同时也可以将json数组对象转换为Person实例。如果我们需要的是一个标准的json树对象而不仅仅是json数组,可以按照下述方式定义隐式对象。

implicit object myPersonFormat2 extends RootJsonFormat[Person] {
    override def write(person: Person):             JsValue = JsObject(
            "name" -> JsString(person.name),
            "age" -> JsNumber(person.age)
        )

    override def read(json: JsValue): Person = json.asJsObject.getFields("name", "age") match {
        case Seq(JsString(name), JsNumber(age)) => new Person(name, age.toInt)
        case _ => deserializationError("Person expected")
    }
}

       上述代码将Person对象转换成如下形式的json树对象,当然也可实现反向转换。

{
  "name": "wsf",
  "age": 26
}

3.5 递归类型转换

       如果是case类属性又包含自身,既递归类型,在定义隐式对象的时候稍有不同,需要显式指明对象的属性,并将jsonFormat的结果传给lazyFormat,我想这里主要是为了防止递归过程中出现无限循环等错误。代码如下:

case class Foo(i: Int, foo: Option[Foo])
object myRecursiveProtocol extends DefaultJsonProtocol {
  implicit val format: JsonFormat[Foo] = lazyFormat(jsonFormat(Foo, "i", "foo"))
}

import myRecursiveProtocol._
val foo: Foo = Foo(1, Some(Foo(2, Some(Foo(3, None)))))
val json = foo.toJson
println(json)

       最终得到的结果如下:

{"i":1,"foo":{"i":2,"foo":{"i":3}}}

3.5 直接操作JSON对象

       有时候可能我们并不需要这么复杂的功能,就想简单的拼接成JSON对象,这时候可以直接创建JsArray或者JsObject对象,按照自己的要求拼接即可。代码如下:

val json = JsArray(JsNumber(1), JsNumber(2), JsNumber(3), JsString("wsf"))
println(json)

val json2 = JsObject(
  "name" -> JsString("wsf"),
  "age" -> JsNumber(26)
)
println(json2)

       结果如下:

[1,2,3,"wsf"]
{"name":"wsf","age":26}

四、总结

       本文简单介绍了spray-json框架在处理json对象时候的一些常规操作和细节,希望能对理解json以及处理json有所帮助,并为Geotrellis中前后台数据交换等打好基础。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

Python练手题,敢来挑战吗?

这到题用到了字符串的所有字母大写和所有字母小写和字符串拼接,复制,用到的函数有 json 将列表中的内容按照指定字符连接成一个字符串,

12100
来自专栏HansBug's Lab

1798: [Ahoi2009]Seq 维护序列seq

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MB Submit: 2930...

31350
来自专栏Albert陈凯

Scala集合练习题

//创建一个List val list0 = List(1,7,9,8,0,3,5,4,6,2) //将list0中每个元素乘以10后生...

49490
来自专栏闻道于事

Java基础类库

50960
来自专栏无所事事者爱嘲笑

js小题目(持续更新)

18640
来自专栏算法修养

PAT 甲级 1060 Are They Equal

1060. Are They Equal (25) 时间限制 50 ms 内存限制 65536 kB 代码长度限制 16000 B ...

32750
来自专栏前端布道

操作数组不要只会for循环

很多时候,我们在操作数组的时候往往就是一个for循环干到底,对数组提供的其它方法视而不见。看完本文,希望你可以换种思路处理数组,然后可以写出更加漂亮、简洁、函数...

31290
来自专栏数据结构与算法

BZOJ3671: [Noi2014]随机数生成器(贪心)

第1行包含5个整数,依次为 x_0,a,b,c,d ,描述小H采用的随机数生成算法所需的随机种子。第2行包含三个整数 N,M,Q ,表示小H希望生成一个1到 N...

8720
来自专栏函数式编程语言及工具

Scalaz(18)- Monad: ReaderWriterState-可以是一种简单的编程语言

  说道FP,我们马上会联想到Monad。我们说过Monad的代表函数flatMap可以把两个运算F[A],F[B]连续起来,这样就可以从程序的意义上形成一种串...

20670
来自专栏mukekeheart的iOS之旅

iOS学习——属性引用self.xx与_xx的区别

 在iOS开发过程中,我们用@proprety声明一个属性后,在代码中我们可以用self.xx与_xx来获取到这个属性。但是一直有一个疑惑,那就是这两个之间有...

433160

扫码关注云+社区

领取腾讯云代金券