我花了太多时间来实现这一点,我是Scala的新手。
基本上,我向API发出请求,并得到以下响应:
[
{
"id": "bde585ea-43ad-4e62-9f20-ea721193e0a5",
"clientId": "account",
"realm":"test-realm-uqrw"
"name": "${client_account}",
"rootUrl": "${authBaseUrl}",
"baseUrl": "/realms/test-realm-uqrw/account/",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"defaultRoles": [
"manage-account",
"view-profile"
],
"redirectUris": [
"/realms/test-realm-uqrw/account/*"
],
"webOrigins": [],
"protocol": "openid-connect",
"attributes": {},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": false,
"nodeReRegistrationTimeout": 0,
"defaultClientScopes": [
"web-origins",
"role_list",
],
"access": {
"view": true,
"configure": true,
"manage": true
}
},
{..another object of the same type, different values },
{..another object of the same type, different values }
]
我只需要从任何一个对象中提取"id"
字段(稍后我将通过realm
属性进行匹配(。有没有一种简单的方法可以将json列表转换为Map[String, Any]
的List[]
?我之所以说Any
,是因为值的类型千差万别——布尔值、字符串、映射、列表。
我尝试了几种方法(内部工具(和Jackson(错误:com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
scala.collection.immutable.List(no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
(,我得到的最接近的是一个奇怪的Tuples
列表,它给了我不正确的结果(因为处理不正确(。
做这件事的简单方法是什么?还是我注定要为这个API响应创建一个自定义类?或者我可以直接遍历这个JSON文档(我只想从该数组中的一个对象中提取一个值(并提取该值吗?
Circe是本地和现代的解决方案之一,在您的情况下,解决方案可能看起来像:
import io.circe._, io.circe.parser._, io.circe.generic.auto._, io.circe.syntax._
case class Response(
id: String,
clientId: String,
realm: String,
name: String,
rootUrl: String,
baseUrl: String,
surrogateAuthRequired: Boolean,
enabled: Boolean,
alwaysDisplayInConsole: Boolean,
clientAuthenticatorType: String,
defaultRoles: List[String],
redirectUris: List[String],
webOrigins: List[String],
protocol: String,
fullScopeAllowed: Boolean,
nodeReRegistrationTimeout: Int,
defaultClientScopes: List[String],
access: Access
)
case class Access(view: Boolean, configure: Boolean, manage: Boolean)
val json =
s"""
|[
| {
| "id": "bde585ea-43ad-4e62-9f20-ea721193e0a5",
| "clientId": "account",
| "realm":"test-realm-uqrw",
| "name": "client_account",
| "rootUrl": "authBaseUrl",
| "baseUrl": "/realms/test-realm-uqrw/account/",
| "surrogateAuthRequired": false,
| "enabled": true,
| "alwaysDisplayInConsole": false,
| "clientAuthenticatorType": "client-secret",
| "defaultRoles": [
| "manage-account",
| "view-profile"
| ],
| "redirectUris": [
| "/realms/test-realm-uqrw/account/*"
| ],
| "webOrigins": [],
| "protocol": "openid-connect",
| "fullScopeAllowed": false,
| "nodeReRegistrationTimeout": 0,
| "defaultClientScopes": [
| "web-origins",
| "role_list"
| ],
|
| "access": {
| "view": true,
| "configure": true,
| "manage": true
| }
| }
|]
|""".stripMargin
println(parse(json).flatMap(_.as[List[Response]]))
打印输出:
Right(List(Response(bde585ea-43ad-4e62-9f20-ea721193e0a5,account,test-realm-uqrw,client_account,authBaseUrl,/realms/test-realm-uqrw/account/,false,true,false,client-secret,List(manage-account, view-profile),List(/realms/test-realm-uqrw/account/*),List(),openid-connect,false,0,List(web-origins, role_list),Access(true,true,true))))
Scatie:https://scastie.scala-lang.org/5OpAUTjSTEWWTrH4X24vAg
最大的优势——与Jackson不同,它不基于运行时反射,而是基于编译时派生。
更新
正如@LuisMiguelMejíaSuárez在评论部分正确建议的那样,如果你想只获取id
字段,你可以在没有完整模型解析的情况下完成,比如:
import io.circe._, io.circe.parser._
val json =
s"""
|[
| {
| "id": "bde585ea-43ad-4e62-9f20-ea721193e0a5"
| },
| {
| "id": "bde585ea-43ad-4e62-9f20-ea721193e0a6"
| }
|]
|""".stripMargin
println(parse(json).map(_.hcursor.values.map(_.map(_.hcursor.downField("id").as[String]))))
打印输出:
Right(Some(Vector(Right(bde585ea-43ad-4e62-9f20-ea721193e0a5), Right(bde585ea-43ad-4e62-9f20-ea721193e0a6))))
Scatie:https://scastie.scala-lang.org/bSSZdLPyTJWcup2KIb4zAw
但要小心——手动JSON操作,这通常用于边缘情况。我建议即使在简单的情况下也要进行模型推导。
使用jsoniter scala FTW!
它在推导中很方便,在运行时效率最高。JSON值的提取是它最大的亮点。
请添加以下依赖项:
libraryDependencies ++= Seq(
// Use the %%% operator instead of %% for Scala.js
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.6.4",
// Use the "provided" scope instead when the "compile-internal" scope is not supported
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.6.4" % "compile-internal"
)
那么,对于仅id
值,不需要为其他值定义字段或数据结构。
只需定义一个最简单的数据结构并立即解析即可:
import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._
import java.util.UUID
val json = """[
| {
| "id": "bde585ea-43ad-4e62-9f20-ea721193e0a5",
| "clientId": "account",
| "realm":"test-realm-uqrw",
| "name": "${client_account}",
| "rootUrl": "${authBaseUrl}",
| "baseUrl": "/realms/test-realm-uqrw/account/",
| "surrogateAuthRequired": false,
| "enabled": true,
| "alwaysDisplayInConsole": false,
| "clientAuthenticatorType": "client-secret",
| "defaultRoles": [
| "manage-account",
| "view-profile"
| ],
| "redirectUris": [
| "/realms/test-realm-uqrw/account/*"
| ],
| "webOrigins": [],
| "protocol": "openid-connect",
| "attributes": {},
| "authenticationFlowBindingOverrides": {},
| "fullScopeAllowed": false,
| "nodeReRegistrationTimeout": 0,
| "defaultClientScopes": [
| "web-origins",
| "role_list",
|
| ],
|
| "access": {
| "view": true,
| "configure": true,
| "manage": true
| }
| }
|]""".stripMargin.getBytes("UTF-8")
case class Response(id: UUID)
implicit val codec: JsonValueCodec[List[Response]] = JsonCodecMaker.make
val responses = readFromArray(json)
println(responses)
println(responses.map(_.id))
预期输出:
List(Response(bde585ea-43ad-4e62-9f20-ea721193e0a5))
List(bde585ea-43ad-4e62-9f20-ea721193e0a5)
如果需要以不同的方式或更高效地处理您的数据,请随时在这里或gitter聊天中寻求帮助。
您可以使用playjson,它非常简单。
import play.api.libs.json._
case class Access(view: Boolean, configure: Boolean, manage: Boolean)
case class Response(
id: String,
clientId: String,
realm: String,
name: String,
rootUrl: String,
baseUrl: String,
surrogateAuthRequired: Boolean,
enabled: Boolean,
alwaysDisplayInConsole: Boolean,
clientAuthenticatorType: String,
defaultRoles: List[String],
redirectUris: List[String],
webOrigins: List[String],
protocol: String,
fullScopeAllowed: Boolean,
nodeReRegistrationTimeout: Int,
defaultClientScopes: List[String],
access: Access
)
val string =
s"""
|[
| {
| "id": "bde585ea-43ad-4e62-9f20-ea721193e0a5",
| "clientId": "account",
| "realm":"test-realm-uqrw",
| "name": "client_account",
| "rootUrl": "authBaseUrl",
| "baseUrl": "/realms/test-realm-uqrw/account/",
| "surrogateAuthRequired": false,
| "enabled": true,
| "alwaysDisplayInConsole": false,
| "clientAuthenticatorType": "client-secret",
| "defaultRoles": [
| "manage-account",
| "view-profile"
| ],
| "redirectUris": [
| "/realms/test-realm-uqrw/account/*"
| ],
| "webOrigins": [],
| "protocol": "openid-connect",
| "fullScopeAllowed": false,
| "nodeReRegistrationTimeout": 0,
| "defaultClientScopes": [
| "web-origins",
| "role_list"
| ],
|
| "access": {
| "view": true,
| "configure": true,
| "manage": true
| }
| }
|]
|""".stripMargin
implicit val ac = Json.format[Access]
implicit val res = Json.format[Response]
println(Json.parse(string).asInstanceOf[JsArray].value.map(_.as[Response]))
以避免异常-
val responseOpt = Json.parse(string) match {
case JsArray(value: collection.IndexedSeq[JsValue]) => value.map(_.asOpt[Response])
case _ => Seq.empty
}
请参阅:https://scastie.scala-lang.org/RBUHhxxIQAGcKgk9a9iwIA
这是医生:https://www.playframework.com/documentation/2.8.x/ScalaJson
使用play-json的另一个选项是定义一个路径:
val jsPath = JsPath \ "id"
然后应用它:
jsPath(Json.parse(jsonString))
代码在Scastie运行。