JSON basics

本文详细介绍了Play框架中的JSON库,包括如何构建、解析和转换JSON数据,以及如何使用该库进行数据验证。

现代的Web应用经常需要解析并生成JSON格式的数据 (JavaScript 对象符号)。Play通过它的JSON library支持这个功能。

JOSN是一个轻量级的数据交换格式,像下面这样:

{
"name" : "Watership Down",
"location" : {
"lat" : 51.235685,
"long" : -1.309197
},
"residents" : [ {
"name" : "Fiver",
"age" : 4,
"role" : null
}, {
"name" : "Bigwig",
"age" : 6,
"role" : "Owsla"
} ]
}

想学习更多关于JSON的知识,详见json.org.

Play的JSON库

play.api.libs.json 包包含了为展示JSON数据的数据结构及这些数据结构和其他数据展现实现之间的实用工具。这个包的一些功能是:

  • 使用最小样板自动转换为Case类和从Case类转换为JSON。如果你想用最小的代码快速的起步和运行,这可能是个开始的地方
  • 解析时自定义验证。
  • 在请求Body中对JSON的自动解析,如果内容不可被解析或提供了不正确的Content-type会自动生成错误。
  • 可以作为独立的可在Play应用以外的地方使用。只需要在你的 build.sbt文件中加上libraryDependencies += "com.typesafe.play" %% "play-json" % playVersion。
  • 高度可自定义。

这个包提供了下列类型:

JsValue

这是表示任何JSON值的特质。JSON库扩展了JsValue的Case类来展示每一个有效的JSON类型:

使用多样的JsValue类型,你可以构建任何JSON结构的展现形式。

Json

Json对象主要为JsValue结构和对象转换提供了实用工具.

JsPath

表示JsValue结构内的路径,类似于XML的XPath。这是用来遍历 JsValue 结构和模式的隐式转换器。

转换为JsValue

使用字符串解析

import play.api.libs.json._

val json: JsValue = Json.parse("""
{
"name" : "Watership Down",
"location" : {
"lat" : 51.235685,
"long" : -1.309197
},
"residents" : [ {
"name" : "Fiver",
"age" : 4,
"role" : null
}, {
"name" : "Bigwig",
"age" : 6,
"role" : "Owsla"
} ]
}
""")

使用类构造

import play.api.libs.json._

val json: JsValue = JsObject(Seq(
"name" -> JsString("Watership Down"),
"location" -> JsObject(Seq("lat" -> JsNumber(51.235685), "long" -> JsNumber(-1.309197))),
"residents" -> JsArray(Seq(
JsObject(Seq(
"name" -> JsString("Fiver"),
"age" -> JsNumber(4),
"role" -> JsNull
)),
JsObject(Seq(
"name" -> JsString("Bigwig"),
"age" -> JsNumber(6),
"role" -> JsString("Owsla")
))
))
))

Json.objJson.arr 的构造可以再简化一点。注意大多数的值不需要被JsValue 类明确的封装,工厂方法使用了隐式转换(更多信息在下面)。

import play.api.libs.json.{JsNull,Json,JsString,JsValue}

val json: JsValue = Json.obj(
"name" -> "Watership Down",
"location" -> Json.obj("lat" -> 51.235685, "long" -> -1.309197),
"residents" -> Json.arr(
Json.obj(
"name" -> "Fiver",
"age" -> 4,
"role" -> JsNull
),
Json.obj(
"name" -> "Bigwig",
"age" -> 6,
"role" -> "Owsla"
)
)
)

使用Writes转换器

ScalaJsValue转换是通过Json.toJson[T](T)(implicit writes: Writes[T])实用方法执行的。这个功能取决于可以吧T转换为JsValueWrites[T]类型的转换器。

Play的JSON API为大多数基本类型提供隐式的Writes,如Int, Double, String, 和 Boolean.它也支持有Writes[T]的任何类型T的集合。

import play.api.libs.json._

// basic types
val jsonString = Json.toJson("Fiver")
val jsonNumber = Json.toJson(4)
val jsonBoolean = Json.toJson(false)

// collections of basic types
val jsonArrayOfInts = Json.toJson(Seq(1, 2, 3, 4))
val jsonArrayOfStrings = Json.toJson(List("Fiver", "Bigwig"))
To convert your own models to JsValues, you must define implicit Writesconverters and provide them in scope.
case class Location(lat: Double, long: Double)
case class Resident(name: String, age: Int, role: Option[String])
case class Place(name: String, location: Location, residents: Seq[Resident])
import play.api.libs.json._

implicit val locationWrites = new Writes[Location] {
def writes(location: Location) = Json.obj(
"lat" -> location.lat,
"long" -> location.long
)
}

implicit val residentWrites = new Writes[Resident] {
def writes(resident: Resident) = Json.obj(
"name" -> resident.name,
"age" -> resident.age,
"role" -> resident.role
)
}

implicit val placeWrites = new Writes[Place] {
def writes(place: Place) = Json.obj(
"name" -> place.name,
"location" -> place.location,
"residents" -> place.residents)
}

val place = Place(
"Watership Down",
Location(51.235685, -1.309197),
Seq(
Resident("Fiver", 4, None),
Resident("Bigwig", 6, Some("Owsla"))
)
)

val json = Json.toJson(place)

另外,你可以使用组合模式定义你的Writes

注意:组合模式在 JSON Reads/Writes/Formats Combinators详细介绍。

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val locationWrites: Writes[Location] = (
(JsPath \ "lat").write[Double] and
(JsPath \ "long").write[Double]
)(unlift(Location.unapply))

implicit val residentWrites: Writes[Resident] = (
(JsPath \ "name").write[String] and
(JsPath \ "age").write[Int] and
(JsPath \ "role").writeNullable[String]
)(unlift(Resident.unapply))

implicit val placeWrites: Writes[Place] = (
(JsPath \ "name").write[String] and
(JsPath \ "location").write[Location] and
(JsPath \ "residents").write[Seq[Resident]]
)(unlift(Place.unapply))

遍历JsValue结构

你可以遍历JsValue结构并提取特定的值。语法和功能类似于Scala XML的处理。

注意:下面的例子使用了前面例子中创建的JsValue结构。

简单路径\

对JsValue使用\ 操作将返回与字段参数相对应的属性,假如这是一个JsObject。

val lat = (json \ "location" \ "lat").get
// returns JsNumber(51.235685)

遍历路径 \

使用 \操作将会在当前对象和所有子中的寻找字段。

val names = json \\ "name"
// returns Seq(JsString("Watership Down"), JsString("Fiver"), JsString("Bigwig"))

索引查找(适用于JsArrays)

你可以在JsArray中使用带索引的操作取值。

val bigwig = (json \ "residents")(1)
// returns {"name":"Bigwig","age":6,"role":"Owsla"}

从JsValue转换

使用字符串工具

Minified:

val minifiedString: String = Json.stringify(json)
{"name":"Watership Down","location":{"lat":51.235685,"long":-1.309197},"residents":[{"name":"Fiver","age":4,"role":null},{"name":"Bigwig","age":6,"role":"Owsla"}]}

Readable:

val readableString: String = Json.prettyPrint(json)
{
"name" : "Watership Down",
"location" : {
"lat" : 51.235685,
"long" : -1.309197
},
"residents" : [ {
"name" : "Fiver",
"age" : 4,
"role" : null
}, {
"name" : "Bigwig",
"age" : 6,
"role" : "Owsla"
} ]
}

使用JsValue.as/asOpt

把JsValue转换为其他的类型的最简单的方式是使用JsValue.as[T](implicit fjs: Reads[T]): T。这需要一个隐式的Reads[T] 类型的转换器把JsValue转换成T(Writes[T]的反向转换)。和Writes一样,JSON API为基本类型提供了Reads

val name = (json \ "name").as[String]
// "Watership Down"

val names = (json \\ "name").map(_.as[String])
// Seq("Watership Down", "Fiver", "Bigwig")

如果路径没找到或不能转换 as方法将抛出JsResultException。安全的方法是JsValue.asOpt[T](implicit fjs: Reads[T]): Option[T].

val nameOption = (json \ "name").asOpt[String]
// Some("Watership Down")

val bogusOption = (json \ "bogus").asOpt[String]
// None

尽管asOpt方法是安全的,但是错误信息将会丢失。

使用验证

从JsValue 转换为其他类型的最好的方式是使用它的validate方法(使用Reads类型的参数)。这个方法及进行验证也进行转换,返回JsResult类型的值。JsResult被两个类实现了:

  • JsSuccess: 表示成功验证/转换并封装结果。
  • JsError: 表示不成功的验证/转换并包含验证错误的列表。 你可以使用多种模式处理验证结果:
val json = { ... }

val nameResult: JsResult[String] = (json \ "name").validate[String]

// Pattern matching
nameResult match {
case s: JsSuccess[String] => println("Name: " + s.get)
case e: JsError => println("Errors: " + JsError.toJson(e).toString())
}

// Fallback value
val nameOrFallback = nameResult.getOrElse("Undefined")

// map
val nameUpperResult: JsResult[String] = nameResult.map(_.toUpperCase())

// fold
val nameOption: Option[String] = nameResult.fold(
invalid = {
fieldErrors => fieldErrors.foreach(x => {
println("field: " + x._1 + ", errors: " + x._2)
})
None
},
valid = {
name => Some(name)
}
)

JsValue 转换成模型

为了把JsValue 转换为模型,你必须定义隐式的Reads[T],在这里T是你的模型的类型。

注意:曾经实现了Reads并自定义了验证的模式在JSON Reads/Writes/Formats Combinators中有详细介绍。

case class Location(lat: Double, long: Double)
case class Resident(name: String, age: Int, role: Option[String])
case class Place(name: String, location: Location, residents: Seq[Resident])
import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val locationReads: Reads[Location] = (
(JsPath \ "lat").read[Double] and
(JsPath \ "long").read[Double]
)(Location.apply _)

implicit val residentReads: Reads[Resident] = (
(JsPath \ "name").read[String] and
(JsPath \ "age").read[Int] and
(JsPath \ "role").readNullable[String]
)(Resident.apply _)

implicit val placeReads: Reads[Place] = (
(JsPath \ "name").read[String] and
(JsPath \ "location").read[Location] and
(JsPath \ "residents").read[Seq[Resident]]
)(Place.apply _)


val json = { ... }

val placeResult: JsResult[Place] = json.validate[Place]
// JsSuccess(Place(...),)

val residentResult: JsResult[Resident] = (json \ "residents")(1).validate[Resident]
// JsSuccess(Resident(Bigwig,6,Some(Owsla)),)

转载于:https://my.oschina.net/u/587323/blog/956648

### 如何在 QML 中解析并显示 JSON 数据 要在 QML 中解析并显示 JSON 数据,可以通过以下方法实现: #### 方法概述 1. **C++ 侧准备 JSON 数据** 使用 Qt 的 `QFile` 和 `QJsonDocument` 类加载和解析本地 JSON 文件,并将数据转换为 `QVariantMap` 或 `QJsonObject` 后传递给 QML。 2. **QML 侧接收数据** 利用 `setContextProperty()` 函数,在 C++ 中将 JSON 数据注册到 QML 上下文中。随后可以在 QML 中通过指定名称访问该数据。 3. **使用模型视图组件展示数据** 借助 QML 的 `ListView`、`Repeater` 等控件以及 `modelData` 属性,可以动态渲染 JSON 数据的内容。 --- #### 示例代码 ##### C++ 部分 (加载 JSON 并传递至 QML) ```cpp #include <QFile> #include <QJsonDocument> #include <QJsonObject> #include <QQmlContext> void loadCourses(QQmlContext *context) { QFile file("courses.json"); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Failed to open courses.json"; return; } QByteArray jsonData = file.readAll(); QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); if (!jsonDoc.isObject()) { qWarning() << "Invalid JSON format"; return; } QJsonObject jsonObject = jsonDoc.object(); context->setContextProperty("courseModel", jsonObject); // 注册到 QML } ``` 此部分实现了从文件中读取 JSON 数据,并将其作为上下文属性传递给 QML[^2]。 --- ##### QML 部分 (解析并显示 JSON 数据) 假设 JSON 数据结构如下: ```json { "title": "Programming Courses", "modules": [ {"name": "Python Basics"}, {"name": "Advanced Python"} ] } ``` 对应的 QML 实现如下: ```qml import QtQuick 2.15 import QtQuick.Controls 2.15 ApplicationWindow { visible: true width: 400 height: 300 title: courseModel.title ?? "No Title" ColumnLayout { anchors.fill: parent spacing: 10 Text { text: "Modules:" font.bold: true } ListView { Layout.fillWidth: true Layout.preferredHeight: contentHeight model: courseModel.modules ? courseModel.modules : [] delegate: Rectangle { color: index % 2 === 0 ? "#f0f0f0" : "#dcdcdc" border.color: "black" height: 30 Text { anchors.centerIn: parent text: name } } } } } ``` 在此示例中,`courseModel` 是由 C++ 设置的上下文属性名。它被用于设置窗口标题 (`courseModel.title`) 和填充列表视图 (`courseModel.modules`)[^1]。 --- #### 关键点解释 1. **JSON 数据传递** - 在 C++ 中调用 `setContextProperty()` 将 JSON 对象注册为 QML 的全局变量。 - 此操作使得 QML 能够像普通 JavaScript 对象一样访问 JSON 数据[^2]。 2. **动态绑定与渲染** - 使用 `ListView` 组件结合 `delegate` 定义每项的外观样式。 - 模型数据可通过 `model` 属性绑定,而具体字段则通过 `modelData.name` 这样的方式访问[^1]。 3. **错误处理** - 如果 JSON 结构不完整或缺失某些字段,则应提供默认值以防止运行时崩溃。例如:`courseModel.title ?? "No Title"` 表达式会在缺少 `title` 字段时返回 `"No Title"`[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值