@xtccc
2017-06-26T13:05:11.000000Z
字数 14332
阅读 4569
开发技巧
目录:
参考 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.Gsonval gson = new Gson()val a = new Array[Int](5)for (i <- 0 until a.size)a(i) = i * 100var 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.DefaultJsonProtocolcase 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.toJsonval jsonString = json.toString()println(jsonString)}}
Def.scala
import spray.httpx.SprayJsonSupportimport spray.json.DefaultJsonProtocolcase 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.HttpServiceimport JsonProtocol._import spray.json._class Server extends Actor with RouteService with ActorLogging {def actorRefFactory = contextdef 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.DefaultJsonProtocolimport 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 Treeval ast: JsValue = json.parseJson// convert AST to objectval 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.sprayjsonimport org.joda.time.DateTimeZoneimport 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.toJsonval jString = json.toStringprintln("序列化后的字符为:")println(jString)println("\n反序列化的结果为:")val ast = jString.parseJsonval 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 => ({ // deserializecase 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]))},{ // srializecase 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.Typeimport com.google.gson._val car = Car("BMW", "X5", 3.0F, 88F, Array[String]("张三", "李四", "王二"), false)val gsonBuilder = new GsonBuilder// 为该class注册自定义的json serializergsonBuilder.registerTypeAdapter(classOf[Car], new CarSerializer)gsonBuilder.setPrettyPrintingval gson = gsonBuilder.createval json = gson.toJson(car)println(json)// 给定的classcase class Car(brand: String, series: String, displacement: Float,price: Float, owners: Array[String], newCar: Boolean)// 为类Car自定义json serializerclass CarSerializer extends JsonSerializer[Car] {override def serialize(car: Car, typeOfSrc: Type,context: JsonSerializationContext): JsonElement = {val ret = new JsonObjectret.addProperty("品 牌", car.brand)ret.addProperty("是否新车", if (car.newCar) "Yes" else "No")ret.addProperty("历任车主数量", car.owners.size)val jsonArr = new JsonArrayfor (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.JsonMethodsimplicit val format = org.json4s.DefaultFormatsval 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.DateTimeZoneimport org.json4s._import org.json4s.jackson.Json/*** Created by tao on 4/10/17.*/object SerializeFields {implicit val formats = org.json4s.DefaultFormatsval 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.DateTimeZoneimport org.json4s._import org.json4s.jackson.Jsonobject SerializeFields {implicit val formats = org.json4s.DefaultFormats + DateTimeZoneSerializerval 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.EnumNameSerializerimport org.json4s.jackson.Jsonimport 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.Monval json = JSON.writePretty(Map("TODAY" -> today))println(json)}def testDeserialization(): Unit = {val json ="""{| "TODAY" : "Monday"|}""".stripMarginval today = parse(json).extract[Map[String, WeekDay]]println(today)}}///////////////////////////////////////////////////////////object WeekDay extends Enumeration {type WeekDay = Valueval 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.DefaultFormatsdef 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.DefaultFormatsJObject(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.DefaultFormatsJObject(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.DefaultFormatsJObject({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 + MySerializerdef 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"}