@xtccc
2017-06-26T21:05:11.000000Z
字数 14332
阅读 4003
开发技巧
目录:
参考 Read JSON with JAVA using Google-gson library
public class Student {
public String name;
public String age;
public String home;
public String gender;
public Student() {
}
public Student(String name, String age, String home, String gender) {
this.name = name;
this.age = age;
this.home = home;
this.gender = gender;
}
}
public static void main(String[] args) {
Student t = new Student("xiao", "31", null, "");
String json = obj2Json(t);
System.out.println(json);
System.out.println("--------------------");
IterateJson(json);
}
public static String obj2Json(Student t) {
return new Gson().toJson(t);
}
/**
* 遍历JSON中的元素(只在第一层遍历)
*/
public static void IterateJson(String json) {
JsonParser parser = new JsonParser();
JsonElement jsonElem = parser.parse(new StringReader(json));
JsonObject jsonObj = (JsonObject)jsonElem;
Set<Map.Entry<String, JsonElement>> ens = jsonObj.entrySet();
for (Map.Entry<String, JsonElement> en : ens) {
System.out.println(en.getKey() + " -> " + en.getValue());
}
}
运行结果为:
对于常见的基本数据类型(如Array, Map),可以将其直接转换为JSON。
import com.google.gson.Gson
val gson = new Gson()
val a = new Array[Int](5)
for (i <- 0 until a.size)
a(i) = i * 100
var json = gson.toJson(a)
println(s"a is converted to $json")
val b = new java.util.TreeMap[String, Array[Int]]()
b.put("张三", Array.fill[Int](3)(3))
b.put("李四", Array.fill[Int](4)(4))
b.put("王五", Array.fill[Int](5)(5))
json = gson.toJson(b)
println(s"b is converted to $json")
运行结果为:
如果是较为特殊的数据类型,例如:
{ "name" : "Jack",
"weight": 76.6,
"salaries" : {
"January" : 11111.1,
"February": 22222.2,
"March" : 33333.3
},
"children" : [
"Tom","Merry","Rose"
]
}
我们可以使用case class来很方便地实现以上需求。
/** 使用case class来定义我们的custom data type */
case class Person(name: String, weight: Float,
salaries: util.HashMap[String, Float],
children: Array[String]) {}
def Serialize_CustomTypes(): Unit = {
val salaries = new util.HashMap[String, Float]()
salaries.put("January" , 11111.1f)
salaries.put("February" , 22222.2f)
salaries.put("March" , 33333.3f)
val children = Array("Tom", "Merry", "Rose")
val jack = Person("Jack", 76.6f, salaries, children)
val json = new Gson().toJson(jack)
println(s"jack is converted to $json")
}
注意: case class的定义不要放在 Serialize_CustomTypes
中,否则会报异常 java.lang.InternalError: Malformed class name
在Scala语言中,利用spray-json可以更加简单地处理JSON,参考 Scala and JSON generation
CaseClass.scala
import spray.json.DefaultJsonProtocol
case class Record(id: Int, description: String)
case class MoreRecord(ip: String, r: Record)
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val recordFormat = jsonFormat2(Record)
implicit val moreRecordFormat = jsonFormat2(MoreRecord)
}
TestSprayJson.scala
import MyJsonProtocol._
import spray.json._
object TestSprayJson {
def main(args: Array[String]): Unit = {
val record = Record(1, "description here")
val moreRecord = MoreRecord("127.0.0.1", record)
val json: JsValue = moreRecord.toJson
val jsonString = json.toString()
println(jsonString)
}
}
Def.scala
import spray.httpx.SprayJsonSupport
import spray.json.DefaultJsonProtocol
case class RequestClusterParams(
accessKey : String,
secreteKey : String,
userName : String,
region : String,
price : String,
slaveNodes : String,
instanceType : String,
amiID : String,
launchGroup : String,
securityKey : String,
securityGroup : String,
subnetID : String)
case class SubmitRequestResult(
status : Boolean,
message : String,
params : RequestClusterParams )
object JsonProtocol extends DefaultJsonProtocol with SprayJsonSupport{
implicit val RequestClusterParamsFormat = jsonFormat12(RequestClusterParams)
implicit val SubmitRequestResultFormat = jsonFormat3(SubmitRequestResult)
}
SprayServer.scala
import akka.actor.{Actor, ActorLogging}
import spray.http.{HttpEntity, _}
import spray.routing.HttpService
import JsonProtocol._
import spray.json._
class Server extends Actor with RouteService with ActorLogging {
def actorRefFactory = context
def receive = runRoute(myRoute)
}
trait RouteService extends HttpService {
this: Server =>
val myRoute = { pathPrefix("v1") {
path("request-path") {
(post & entity(as[RequestClusterParams])) { params =>
val result = SubmitRequestResult(····)
complete(HttpResponse(StatusCodes.OK,
HttpEntity(ContentTypes.`application/json`, result.toJson.prettyPrint.getBytes)))
}
}
}
}
}
当收到的POST请求的BODY为一个JSON字符串时,会试图将其转换成对应的RequestClusterParams
;当我们在程序中创建一个类型为RequestClusterParams
的实例result之后,通过result.toJson.toString可以将其转换成JSON字符串。
值得注意的是,如果在定义object JsonProtocol
时,没有让它扩展trait SprayJsonSupport
,则可以会出现错误:
could not find implicit value for parameter um: spray.httpx.unmarshalling.FromRequestUnmarshaller[RequestClusterParams]
(post & entity(as[RequestClusterParams])) { params =>
因为trait SprayJsonSupport
提供了自动的JSON marshalling/unmarshalling
import spray.json.DefaultJsonProtocol
import spray.json._
case class CA(flag: String, list: List[Int])
case class Bean(a: String, b: Int, ca: CA)
object BeanJsonProtocol extends DefaultJsonProtocol {
implicit val CAFormat = jsonFormat2(CA)
implicit val BeanFormat = jsonFormat3(Bean)
}
import BeanJsonProtocol._
object Deserialize extends App {
val json =
"""
{ "a": "A",
"b": 100,
"ca" : {
"flag" : "FLAG!!",
"list": [1,2,3,4,5]
}
}"""
// get Abstract Syntax Tree
val ast: JsValue = json.parseJson
// convert AST to object
val obj: Bean = ast.convertTo[Bean]
println(obj)
}
上面展示了怎样处理Scala中的case class,如果想处理其他的class呢?例如想处理Java实现的org.joda.time.DateTimeZone
类。。。
我们需要自己为org.joda.time.DateTimeZone
实现一个format,这个format里面要实现write
(序列化)和read
两种方法(反序列化)。
由于DateTimeFormat的核心数据是id
,因此我们只需要利用id即可创建出一个新的实例。
package cn.gridx.scala.lang.json.sprayjson
import org.joda.time.DateTimeZone
import spray.json._
object DateTimeZoneFormat extends RootJsonFormat[DateTimeZone] {
// 序列化
def write(tz: DateTimeZone): JsValue = {
JsObject("id" -> JsString(tz.getID))
}
// 反序列化
def read(value: JsValue): DateTimeZone = {
value.asJsObject.getFields("id") match {
case Seq(JsString(id)) =>
DateTimeZone.forID(id)
case _ =>
throw new RuntimeException("ERROR")
}
}
}
object JavaJsonProtocols extends DefaultJsonProtocol {
implicit val __DateTimeZone = DateTimeZoneFormat
}
/**
* Created by tao on 1/11/17.
*/
object customization extends App {
import JavaJsonProtocols._
val tz = DateTimeZone.forID("America/Los_Angeles")
val json = tz.toJson
val jString = json.toString
println("序列化后的字符为:")
println(jString)
println("\n反序列化的结果为:")
val ast = jString.parseJson
val obj = ast.convertTo[DateTimeZone]
println(obj)
}
运行结果为
序列化后的字符为:
{"id":"America/Los_Angeles"}反序列化的结果为:
America/Los_Angeles
也可以通过json4s来实现对non-case class的序列化和反序列化
参考 Serializing non-supported types
import org.json4s._
import org.json4s.native.Serialization.{write, writePretty}
class A(val a1: String, val a2: Int, val a3: Option[Boolean]) {
}
class SA extends CustomSerializer[A](format => (
{ // deserialize
case JObject(JField("a1", JString(a1)) :: JField("a2", JInt(a2))
:: JField("a3", JObject(a3)) :: Nil ) =>
new A(a1, a2.intValue(),
if (a3 == JNothing) None else Some(a3.asInstanceOf[Boolean]))
},
{ // srialize
case a: A =>
JObject(JField("a1", JString(a.a1)) ::
JField("a2", JInt(a.a2)) ::
JField("a3", if (a.a3.isEmpty) JNothing else JBool(a.a3.get)) ::
Nil)
}
)
)
object TestSerialize {
def main(args: Array[String]): Unit = {
implicit val formats = DefaultFormats + new SA()
val a = new A("AAAA", 100, None)
println(write(a))
println(writePretty(a))
}
}
在将一个class转换成json时,key一般就是class field的名字。如果希望将key进行自定义,那么可以通过@SerializedName
的方式来实现 :
public class ST {
@SerializedName("my name")
public String Name;
@SerializedName("my age")
public int Age;
public ST(String name, int age) {
this.Name = name;
this.Age = age;
}
};
String json = new Gson().toJson(new ST("Jack Tomcat", 300));
生成的json是:
{"my name":"Jack Tomcat","my age":300}
参考
重命名field name是非常简单的需求,我们可以通过JsonSerializer来实现对一个给定class定义更加复杂的序列化机制,使得在将该class转化为JSON串时,能够自定义其格式和内容(包括:rename fields, add/delete fields, format contents等)。
import java.lang.reflect.Type
import com.google.gson._
val car = Car("BMW", "X5", 3.0F, 88F, Array[String]("张三", "李四", "王二"), false)
val gsonBuilder = new GsonBuilder
// 为该class注册自定义的json serializer
gsonBuilder.registerTypeAdapter(classOf[Car], new CarSerializer)
gsonBuilder.setPrettyPrinting
val gson = gsonBuilder.create
val json = gson.toJson(car)
println(json)
// 给定的class
case class Car(brand: String, series: String, displacement: Float,
price: Float, owners: Array[String], newCar: Boolean)
// 为类Car自定义json serializer
class CarSerializer extends JsonSerializer[Car] {
override def serialize(car: Car, typeOfSrc: Type,
context: JsonSerializationContext): JsonElement = {
val ret = new JsonObject
ret.addProperty("品 牌", car.brand)
ret.addProperty("是否新车", if (car.newCar) "Yes" else "No")
ret.addProperty("历任车主数量", car.owners.size)
val jsonArr = new JsonArray
for (owner <- car.owners)
jsonArr.add(new JsonPrimitive(owner))
ret.add("车主 (包含二手车主)", jsonArr)
val gson = new Gson()
// 加入Map类型的项
// `JsonObject#add` 可以加入一个 `JsonElement` 类型的property
// 借此, 可以加入map等复杂类型
val map = new util.HashMap[String, Double]()
map.put("人民币", car.price)
map.put("美 元", car.price/8)
map.put("英 镑", car.price/10.4)
ret.add("价 格", gson.toJsonTree(map))
ret
}
}
输出为:
{
"品 牌": "BMW",
"是否新车": "No",
"历任车主数量": 3,
"车主 (包含二手车主)": [
"张三",
"李四",
"王二"
],
"价 格": {
"人民币": 880000.0,
"英 镑": 84615.38461538461,
"美 元": 110000.0
}
}
#
JSON4S是最灵活、最强大的json工具,值得一看。
参考
import org.json4s._
import org.json4s.jackson.JsonMethods
implicit val format = org.json4s.DefaultFormats
val obj = JsonMethods.parse("""["a","b"]""").extract[List[String]]
println(obj)
有时,一个case class里面的某个field是自定义的类型,没有现成的serializer,我们可以为它单独添加一个serializer。
例如,DateTimeZone
没有现成的serializer,如果它是某个class的field,则该class无法直接被json4s序列化:
import org.joda.time.DateTimeZone
import org.json4s._
import org.json4s.jackson.Json
/**
* Created by tao on 4/10/17.
*/
object SerializeFields {
implicit val formats = org.json4s.DefaultFormats
val JSON = Json(formats)
def main(args: Array[String]): Unit = {
val req = GlobalDataRequest("A", 100, DateTimeZone.forID("America/Los_Angeles"))
val json: String = JSON.writePretty(req)
println(json)
val x = JSON.parse(json).extract[GlobalDataRequest]
println(x)
}
}
case class GlobalDataRequest(a: String, b: Int, c: DateTimeZone)
运行结果:
现增加一个field serializer:
import org.joda.time.DateTimeZone
import org.json4s._
import org.json4s.jackson.Json
object SerializeFields {
implicit val formats = org.json4s.DefaultFormats + DateTimeZoneSerializer
val JSON = Json(formats)
def main(args: Array[String]): Unit = {
val req = GlobalDataRequest("A", 100, DateTimeZone.forID("America/Los_Angeles"))
val json: String = JSON.writePretty(req)
println(json)
val x = JSON.parse(json).extract[GlobalDataRequest]
println(x)
}
}
/**
* 这就是field serializer
*/
case object DateTimeZoneSerializer extends CustomSerializer[DateTimeZone](format => (
{
case JString(s) => DateTimeZone.forID(s)
case JNull => null
},
{
case tz: DateTimeZone => JString(tz.getID)
}
))
case class GlobalDataRequest(a: String, b: Int, c: DateTimeZone)
现在可以通过json4s正常地对GlobalDataRequest
实现序列化和反序列化。
对Scala Enumeration的序列化和反序列化不能通过自定义的Filed Serializer来实现,而是需要借助于org.json4s.ext.EnumNameSerializer
。
参考: Serialize and Deserialize scala enumerations or case objects using json4s
import org.json4s._
import org.json4s.ext.EnumNameSerializer
import org.json4s.jackson.Json
import org.json4s.jackson.JsonMethods._
object TestEnumeration {
implicit lazy val formats = org.json4s.DefaultFormats + new EnumNameSerializer(WeekDay)
val JSON = Json(formats)
def main(args: Array[String]): Unit = {
testSerialization()
testDeserialization()
}
def testSerialization(): Unit = {
val today = WeekDay.Mon
val json = JSON.writePretty(Map("TODAY" -> today))
println(json)
}
def testDeserialization(): Unit = {
val json =
"""{
| "TODAY" : "Monday"
|}
""".stripMargin
val today = parse(json).extract[Map[String, WeekDay]]
println(today)
}
}
///////////////////////////////////////////////////////////
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon = Value(0, "Monday")
val Tue = Value(1, "Tuesday")
val Wed = Value(2, "Wednesday")
val Thu = Value(3, "Tuesday")
val Fri = Value(4, "Friday")
val Sat = Value(5, "Saturday")
val Sun = Value(6, "Sunday")
}
假如有这样一个JSON string:
val str = """
|{
| "error": "not_authorised",
| "reason": "User not authorised to access virtual host",
| "extra" : {
| "a" : "A",
| "b" : "B"
| }
|
|} """.stripMargin
我们只想取出其中的extra
,其他的不关心,怎么做?
val json = parse(str)
val value_error : JValue = json\\"error"
val value_errors: JValue = json\\"errors"
val value_extra : JValue = json\\"extra"
println("field error : " + value_error.extractOpt[String])
println("field reason : " + value_errors.extractOpt[String])
println("field extra : " + value_extra.extractOpt[Map[String, String]])
输出结果为:
field error : Some(not_authorised)
field reason : None
field extra : Some(Map(a -> A, b -> B))
同名field的带来的问题
对于下面的JSON:
{
"meta": {
"code": 200
},
"data": {
"results": {
"status": "success",
"responseTime": 3452,
"billStatus": [
{
"status": "success"
},
{
"status": "success"
}
]
}
}
}
使用下面的代码没法取出 data\results\status
:
val jVal = org.json4s.jackson.JsonMethods.parse(respBody)
val status = (jVal \\ "data" \\ "results" \\ "status").extractOpt[String]
println(s"status = $status")
输出是 None.
解决方法
使用单反斜杠(\),而不是双反斜杠(\):
val jVal = org.json4s.jackson.JsonMethods.parse(respBody)
val status = (jVal \ "data" \ "results" \ "status").extractOpt[String]
println(s"status = $status")
对于有些类型,JSON4S不能自动地正确实现序列化,例如HashMap和java.util.TreeMap
,举例:
object TestJson {
implicit val formats = org.json4s.DefaultFormats
def main(args: Array[String]): Unit = {
val tree = new java.util.TreeMap[Any, Any]()
tree.put("bill_category", "CARE_Combined")
tree.put("bill_presentation_category", "audit")
tree.put("bill_super_category", 12)
val in1 = HashMap("y" -> tree)
val in2 = HashMap(tree -> "x")
println("tree = " + org.json4s.jackson.Serialization.write(tree))
println("in1 = " + org.json4s.jackson.Serialization.write(in1))
println("in2 = " + org.json4s.jackson.Serialization.write(in2))
}
}
运行结果:
我们需要对Map和TreeMap实现自定义的序列化
object MySerializer extends CustomSerializer[Any] (thisFormat =>
(
{
case x =>
throw new RuntimeException("不感兴趣")
},
{
case m: scala.collection.immutable.HashMap[Any, Any] =>
implicit val formats = org.json4s.DefaultFormats
JObject(
m.map({
case (k, v) =>
JField(k.toString, Extraction.decompose(v)(thisFormat))
}).toList
)
case m: scala.collection.mutable.HashMap[Any, Any] =>
implicit val formats = org.json4s.DefaultFormats
JObject(
m.map({
case (k, v) =>
JField(k.toString, Extraction.decompose(v)(thisFormat))
}).toList
)
case m: java.util.TreeMap[Any, Any] =>
implicit val formats = org.json4s.DefaultFormats
JObject({
val con = ArrayBuffer[JField]()
val it = m.keySet().iterator()
while (it.hasNext) {
val key = it.next()
con.append(JField(key.toString, Extraction.decompose(m.get(key))(thisFormat)))
}
con.toList
}
)
}
)
)
object TestJson {
implicit val formats = org.json4s.DefaultFormats + MySerializer
def main(args: Array[String]): Unit = {
... // 相同的测试内容
...
}
}
运行结果:
tree = {"bill_category":"CARE_Combined","bill_presentation_category":"audit","bill_super_category":12}
in1 = {"y":{"bill_category":"CARE_Combined","bill_presentation_category":"audit","bill_super_category":12}}
in2 = {"{bill_category=CARE_Combined, bill_presentation_category=audit, bill_super_category=12}":"x"}