解析 json 并基于值 return 键列表
Parse json and based on value return a list of keys
Scala 专家,需要您的帮助。任务是解析 json 和 return 商店 'livingstone' 的 ModelId 列表,其中 'organic' = true。
在这种情况下,只有 RT001 具有有机价值作为 true.Please 帮助。
注意:尝试使用现有的 liftweb 库。
package Exercises
import net.liftweb.json.{DefaultFormats, _}
object test1 extends App {
val json_response =
"""{
"requestId": "91ee60d5f1b45e#316",
"error": null,
"errorMessages": [
],
"entries": [
{
"modelId":"RT001",
"sku": "SKU-ASC001",
"store": "livingstone",
"ttlInSeconds": 8000,
"metadata": {
"manufactured_date": "2019-01-22T01:25Z",
"organic": "true"
}
},
{
"modelId":"RT002",
"sku": "SKU-ASC002",
"store": "livingstone",
"ttlInSeconds": 8000,
"metadata": {
"manufactured_date": "2019-10-03T01:25Z",
"organic": "false"
}
}
] }"""
val json = parse(json_response)
implicit val formats = DefaultFormats
val elements = (json \ "entries").children
for (subs <- elements) {
val m = subs.extract[Subs]
println(s"Subscriptions: ${m.modelId}, ${m.store}")
println(" k,v: " + m.metadata.exists(_ == ("organic", "true")))
}
case class Subs(modelId: String, store: String, metadata: Map[String, String])
}
获取错误。还需要帮助如何根据 store=living stone 和 organic=true 进行过滤。
Exception in thread "main" net.liftweb.json.MappingException: No usable value for modelId
Do not know how to convert JArray(List(JString(RT001), JString(RT002))) into class java.lang.String
在专家帮助下的最终工作代码:
val json = parse(json_response)
implicit val formats = DefaultFormats
case class Sales(modelId: String, sku: String, store: String, ttlInSeconds: Int, metadata: Map[String, String])
case class Response(entries: List[Sales])
val json1 = parse(json_response)
val response = json.extract[Response]
val subs = response.entries.filter { e =>
e.store == "livingstone" &&
e.metadata.get("organic").contains("true")
}
subs.foreach(x=>println(x.modelId))
因此,正如评论部分所建议的那样,您可以继续使用 circe 库而不是 Lift 框架,因为它是更现代且使用更广泛的解决方案。
您需要做什么 - 声明结构,例如 case class
代表您的 json。这是不推荐的方法,对原始 JSON 进行操作 - 经验法则 - 将其解析为一些有意义的结构,然后使用它。
连同结构声明,你还 Encoder
和 Decoder
比方说 "non standard" 案例 - 比如 Boolean
是 String
organic
字段。
在您的情况下,代码可能如下所示:
object App {
def main(args: Array[String]): Unit = {
import io.circe._, io.circe.generic.semiauto._, io.circe.generic.auto._, io.circe.parser._
/**
* General purpose response wrapper. Entries type might differ, as I can suppose, that's why it is generic.
* 'errorMessages' - since it is empty array in example, I can only guess about exact type. For sake of example
* let's say it is strings. And same for 'error' field.
*/
case class Response[E](requestId: String, error: Option[String], errorMessages: List[String], entries: List[E])
object Response {
implicit def decoder[E](implicit d: Decoder[E]): Decoder[Response[E]] = deriveDecoder[Response[E]]
implicit def encoder[E](implicit e: Encoder[E]): Encoder[Response[E]] = deriveEncoder[Response[E]]
}
case class Product(modelId: String, sku: String, store: String, ttlInSeconds: Int, metadata: ProductMetadata)
case class ProductMetadata(manufactured_date: ZonedDateTime, organic: Boolean)
object ProductMetadata {
// Boolean codec required - because `organic` is a string in JSON, which has boolean type
implicit val booleanDecoder: Decoder[Boolean] = Decoder[String].emapTry(value => Try(value.toBoolean))
implicit val booleanEncoder: Encoder[Boolean] = Encoder[String].contramap(_.toString)
implicit val decoder: Decoder[ProductMetadata] = deriveDecoder[ProductMetadata]
implicit def encoder: Encoder[ProductMetadata] = deriveEncoder[ProductMetadata]
}
val json =
s"""
|{
| "requestId":"91ee60d5f1b45e#316",
| "error":null,
| "errorMessages":[
|
| ],
| "entries":[
| {
| "modelId":"RT001",
| "sku":"SKU-ASC001",
| "store":"livingstone",
| "ttlInSeconds":8000,
| "metadata":{
| "manufactured_date":"2019-01-22T01:25Z",
| "organic":"true"
| }
| },
| {
| "modelId":"RT002",
| "sku":"SKU-ASC002",
| "store":"livingstone",
| "ttlInSeconds":8000,
| "metadata":{
| "manufactured_date":"2019-10-03T01:25Z",
| "organic":"false"
| }
| }
| ]
|}
|""".stripMargin
val parseResult: Either[Error, List[String]] =
for {
parsedJson <- parse(json)
response <- parsedJson.as[Response[Product]]
} yield {
response.entries.collect {
case Product(modelId, _, "livingstone", _, ProductMetadata(_, true)) => modelId
}
}
println(parseResult)
}
这将产生下一个结果
Right(List(RT001))
希望对您有所帮助!
在处理JSON时最好将整个结构转为Scala再处理Scala,而不是直接处理JSON.
因此创建一个 Response
class 并在单个操作中提取它,然后根据需要处理 entries
字段。
这是一些完全未经测试的代码:
case class Response(entries: List[Subs])
val json = parse(json_response)
val response = json.extract[Response]
val subs = response.entries.filter{e =>
e.store == "livingstone" &&
e.metadata.get("organic").contains("true")
}
请注意,这应该适用于任何允许您从 JSON.
中提取 class
的 JSON 库
Scala 专家,需要您的帮助。任务是解析 json 和 return 商店 'livingstone' 的 ModelId 列表,其中 'organic' = true。
在这种情况下,只有 RT001 具有有机价值作为 true.Please 帮助。
注意:尝试使用现有的 liftweb 库。
package Exercises
import net.liftweb.json.{DefaultFormats, _}
object test1 extends App {
val json_response =
"""{
"requestId": "91ee60d5f1b45e#316",
"error": null,
"errorMessages": [
],
"entries": [
{
"modelId":"RT001",
"sku": "SKU-ASC001",
"store": "livingstone",
"ttlInSeconds": 8000,
"metadata": {
"manufactured_date": "2019-01-22T01:25Z",
"organic": "true"
}
},
{
"modelId":"RT002",
"sku": "SKU-ASC002",
"store": "livingstone",
"ttlInSeconds": 8000,
"metadata": {
"manufactured_date": "2019-10-03T01:25Z",
"organic": "false"
}
}
] }"""
val json = parse(json_response)
implicit val formats = DefaultFormats
val elements = (json \ "entries").children
for (subs <- elements) {
val m = subs.extract[Subs]
println(s"Subscriptions: ${m.modelId}, ${m.store}")
println(" k,v: " + m.metadata.exists(_ == ("organic", "true")))
}
case class Subs(modelId: String, store: String, metadata: Map[String, String])
}
获取错误。还需要帮助如何根据 store=living stone 和 organic=true 进行过滤。
Exception in thread "main" net.liftweb.json.MappingException: No usable value for modelId
Do not know how to convert JArray(List(JString(RT001), JString(RT002))) into class java.lang.String
在专家帮助下的最终工作代码:
val json = parse(json_response)
implicit val formats = DefaultFormats
case class Sales(modelId: String, sku: String, store: String, ttlInSeconds: Int, metadata: Map[String, String])
case class Response(entries: List[Sales])
val json1 = parse(json_response)
val response = json.extract[Response]
val subs = response.entries.filter { e =>
e.store == "livingstone" &&
e.metadata.get("organic").contains("true")
}
subs.foreach(x=>println(x.modelId))
因此,正如评论部分所建议的那样,您可以继续使用 circe 库而不是 Lift 框架,因为它是更现代且使用更广泛的解决方案。
您需要做什么 - 声明结构,例如 case class
代表您的 json。这是不推荐的方法,对原始 JSON 进行操作 - 经验法则 - 将其解析为一些有意义的结构,然后使用它。
连同结构声明,你还 Encoder
和 Decoder
比方说 "non standard" 案例 - 比如 Boolean
是 String
organic
字段。
在您的情况下,代码可能如下所示:
object App {
def main(args: Array[String]): Unit = {
import io.circe._, io.circe.generic.semiauto._, io.circe.generic.auto._, io.circe.parser._
/**
* General purpose response wrapper. Entries type might differ, as I can suppose, that's why it is generic.
* 'errorMessages' - since it is empty array in example, I can only guess about exact type. For sake of example
* let's say it is strings. And same for 'error' field.
*/
case class Response[E](requestId: String, error: Option[String], errorMessages: List[String], entries: List[E])
object Response {
implicit def decoder[E](implicit d: Decoder[E]): Decoder[Response[E]] = deriveDecoder[Response[E]]
implicit def encoder[E](implicit e: Encoder[E]): Encoder[Response[E]] = deriveEncoder[Response[E]]
}
case class Product(modelId: String, sku: String, store: String, ttlInSeconds: Int, metadata: ProductMetadata)
case class ProductMetadata(manufactured_date: ZonedDateTime, organic: Boolean)
object ProductMetadata {
// Boolean codec required - because `organic` is a string in JSON, which has boolean type
implicit val booleanDecoder: Decoder[Boolean] = Decoder[String].emapTry(value => Try(value.toBoolean))
implicit val booleanEncoder: Encoder[Boolean] = Encoder[String].contramap(_.toString)
implicit val decoder: Decoder[ProductMetadata] = deriveDecoder[ProductMetadata]
implicit def encoder: Encoder[ProductMetadata] = deriveEncoder[ProductMetadata]
}
val json =
s"""
|{
| "requestId":"91ee60d5f1b45e#316",
| "error":null,
| "errorMessages":[
|
| ],
| "entries":[
| {
| "modelId":"RT001",
| "sku":"SKU-ASC001",
| "store":"livingstone",
| "ttlInSeconds":8000,
| "metadata":{
| "manufactured_date":"2019-01-22T01:25Z",
| "organic":"true"
| }
| },
| {
| "modelId":"RT002",
| "sku":"SKU-ASC002",
| "store":"livingstone",
| "ttlInSeconds":8000,
| "metadata":{
| "manufactured_date":"2019-10-03T01:25Z",
| "organic":"false"
| }
| }
| ]
|}
|""".stripMargin
val parseResult: Either[Error, List[String]] =
for {
parsedJson <- parse(json)
response <- parsedJson.as[Response[Product]]
} yield {
response.entries.collect {
case Product(modelId, _, "livingstone", _, ProductMetadata(_, true)) => modelId
}
}
println(parseResult)
}
这将产生下一个结果
Right(List(RT001))
希望对您有所帮助!
在处理JSON时最好将整个结构转为Scala再处理Scala,而不是直接处理JSON.
因此创建一个 Response
class 并在单个操作中提取它,然后根据需要处理 entries
字段。
这是一些完全未经测试的代码:
case class Response(entries: List[Subs])
val json = parse(json_response)
val response = json.extract[Response]
val subs = response.entries.filter{e =>
e.store == "livingstone" &&
e.metadata.get("organic").contains("true")
}
请注意,这应该适用于任何允许您从 JSON.
中提取class
的 JSON 库