স্ট্যান্ডার্ড স্কালার ক্লাসগুলি ব্যবহার করে কীভাবে স্কালায় জেএসএনকে পার্স করবেন?


113

আমি স্কেল ২.৮-তে জেএসএন কোড পার্স করতে জেএসএন ক্লাসে বিল্ডটি ব্যবহার করছি। নির্ভরতা হ্রাস করার কারণে আমি লিফটব এক বা অন্য কোনওটি ব্যবহার করতে চাই না।

আমি যেভাবে এটি করছি এটি খুব অপরিহার্য বলে মনে হচ্ছে, এটি করার আরও ভাল উপায় আছে কি?

import scala.util.parsing.json._
...
val json:Option[Any] = JSON.parseFull(jsonString)
val map:Map[String,Any] = json.get.asInstanceOf[Map[String, Any]]
val languages:List[Any] = map.get("languages").get.asInstanceOf[List[Any]]
languages.foreach( langMap => {
val language:Map[String,Any] = langMap.asInstanceOf[Map[String,Any]]
val name:String = language.get("name").get.asInstanceOf[String]
val isActive:Boolean = language.get("is_active").get.asInstanceOf[Boolean]
val completeness:Double = language.get("completeness").get.asInstanceOf[Double]
}

উত্তর:


129

এটি এক্সট্র্যাক্টরগুলির উপর ভিত্তি করে একটি সমাধান যা ক্লাস কাস্ট করবে:

class CC[T] { def unapply(a:Any):Option[T] = Some(a.asInstanceOf[T]) }

object M extends CC[Map[String, Any]]
object L extends CC[List[Any]]
object S extends CC[String]
object D extends CC[Double]
object B extends CC[Boolean]

val jsonString =
    """
      {
        "languages": [{
            "name": "English",
            "is_active": true,
            "completeness": 2.5
        }, {
            "name": "Latin",
            "is_active": false,
            "completeness": 0.9
        }]
      }
    """.stripMargin

val result = for {
    Some(M(map)) <- List(JSON.parseFull(jsonString))
    L(languages) = map("languages")
    M(language) <- languages
    S(name) = language("name")
    B(active) = language("is_active")
    D(completeness) = language("completeness")
} yield {
    (name, active, completeness)
}

assert( result == List(("English",true,2.5), ("Latin",false,0.9)))

লুপটির শুরুতে আমি কৃত্রিমভাবে ফলাফলটিকে একটি তালিকায় গুটিয়ে রাখি যাতে এটি শেষে একটি তালিকা দেয়। তারপরে বাকী লুপের বাকী অংশে আমি জেনারেটর (ব্যবহার করে <-) এবং মান সংজ্ঞা (ব্যবহার করে =) প্রয়োগযোগ্য পদ্ধতিগুলি ব্যবহার করব use

(পুরানো উত্তর দূরে সম্পাদিত - আপনি কৌতূহলী হলে সম্পাদনা ইতিহাস পরীক্ষা করুন)


একটি পুরানো পোস্ট খননের জন্য দুঃখিত, তবে লুপে প্রথম কিছু (এম (মানচিত্র)) এর অর্থ কী? আমি বুঝতে পারছি এম (মানচিত্র) পরিবর্তনশীল "মানচিত্রে" মানচিত্রটি বের করছে, তবে কারও কি হবে?
ফেডেরিকো বোনেলি

1
@ ফেডেরিকো বোনেলি JSON.parseFullফিরে আসে Option[Any], তাই এটি দিয়ে List(None)বা দিয়ে শুরু হয় List(Some(any))Someউপর প্যাটার্ন ম্যাচিং জন্য Option
huynhjl

21

আমি প্যাটার্ন ম্যাচটি এভাবেই করি:

val result = JSON.parseFull(jsonStr)
result match {
  // Matches if jsonStr is valid JSON and represents a Map of Strings to Any
  case Some(map: Map[String, Any]) => println(map)
  case None => println("Parsing failed")
  case other => println("Unknown data structure: " + other)
}

আপনি কি আপনার জসনএসটিআর এর উদাহরণ দিতে পারেন, এটি jsonStr এর উপরের উদাহরণটির সাথে কাজ করছে না
প্রিয়া খোকের

এটি আপনার সমস্যা সম্পর্কে নিজস্ব প্রশ্ন পোস্ট করার উপযুক্ত হতে পারে। আমি বর্তমানে আমার মেশিনে স্কালা ইনস্টল করি নি তাই আমার কাছে জেএসএন স্ট্রিং প্রস্তুত নেই।
ম্যাথিয়াস ব্রাউন

12

আমি @ হুইনহজল এর উত্তর পছন্দ করি, এটি আমাকে সঠিক পথে নিয়ে যায়। তবে ত্রুটি শর্ত পরিচালনা করার ক্ষেত্রে এটি দুর্দান্ত নয়। যদি পছন্দসই নোডের অস্তিত্ব না থাকে তবে আপনি একটি কাস্ট ব্যতিক্রম পান। এটিকে Optionহ্যান্ডেল করার জন্য আমি এটিকে সামান্য মানিয়ে নিয়েছি ।

class CC[T] {
  def unapply(a:Option[Any]):Option[T] = if (a.isEmpty) {
    None
  } else {
    Some(a.get.asInstanceOf[T])
  }
}

object M extends CC[Map[String, Any]]
object L extends CC[List[Any]]
object S extends CC[String]
object D extends CC[Double]
object B extends CC[Boolean]

for {
  M(map) <- List(JSON.parseFull(jsonString))
  L(languages) = map.get("languages")
  language <- languages
  M(lang) = Some(language)
  S(name) = lang.get("name")
  B(active) = lang.get("is_active")
  D(completeness) = lang.get("completeness")
} yield {
  (name, active, completeness)
}

অবশ্যই এটি ত্রুটিগুলি এতটা এড়াতে পারে না যতটা এড়ানো যায়। কোনও জাসন নোড অনুপস্থিত থাকলে এটি একটি খালি তালিকা দেবে। matchঅভিনয়ের আগে আপনি নোডের উপস্থিতি যাচাই করতে একটি ব্যবহার করতে পারেন ...

for {
  M(map) <- Some(JSON.parseFull(jsonString))
} yield {
  map.get("languages") match {
    case L(languages) => {
      for {
        language <- languages
        M(lang) = Some(language)
        S(name) = lang.get("name")
        B(active) = lang.get("is_active")
        D(completeness) = lang.get("completeness")
      } yield {
        (name, active, completeness)
      }        
    }
    case None => "bad json"
  }
}

3
আমি মনে করি সিসি প্রয়োগযোগ্যভাবে উল্লেখযোগ্যভাবে সহজ করা যেতে পারে def unapply(a: Option[Any]): Option[T] = a.map(_.asInstanceOf[T])
সুমা

স্কেল 2.12 এর প্রয়োজন মনে হচ্ছে ';' বোঝার জন্য '=' এর সাথে রেখাগুলির আগে।
আকৌপ্পি

আমার জন্য, শীর্ষস্থানীয় কোডটি "জাসন নোডগুলির মধ্যে কোনও অনুপস্থিত থাকলে খালি তালিকা তৈরি করে না", তবে MatchErrorপরিবর্তে একটি স্কেল দিয়েছে (স্কেলা 2.12)। এটির জন্য চেষ্টা / ক্যাপ ব্লকের জন্য মোড়ানো প্রয়োজন। কোন ভাল ধারণা?
আকৌপ্পি

7

কাস্টিং এড়ানোর উপায় হিসাবে প্যাটার্ন ম্যাচিংয়ের পক্ষে আমি কিছু জিনিস চেষ্টা করেছি তবে সংগ্রহের ধরণগুলিতে মুছে ফেলার সমস্যাটিতে পড়েছি।

মূল সমস্যাটি দেখে মনে হচ্ছে যে পার্সের ফলাফলের সম্পূর্ণ ধরণেরটি JSON ডেটার কাঠামোর মিরর করে এবং হয় জটিল বা সম্পূর্ণরূপে বলা অসম্ভব। আমি অনুমান করি যে কেন যে কোনও ধরণের সংজ্ঞা সংশোধন করতে ব্যবহৃত হয়। Any ালাইয়ের প্রয়োজনে যে কোনও লিড ব্যবহার করা ।

আমি নীচে এমন কিছু হ্যাক করেছি যা সংক্ষিপ্ত, তবে প্রশ্নের কোড দ্বারা বর্ণিত JSON ডেটার সাথে অত্যন্ত সুনির্দিষ্ট। আরও সাধারণ কিছু আরও সন্তোষজনক হবে তবে আমি নিশ্চিত নই যে এটি খুব মার্জিত হবে।

implicit def any2string(a: Any)  = a.toString
implicit def any2boolean(a: Any) = a.asInstanceOf[Boolean]
implicit def any2double(a: Any)  = a.asInstanceOf[Double]

case class Language(name: String, isActive: Boolean, completeness: Double)

val languages = JSON.parseFull(jstr) match {
  case Some(x) => {
    val m = x.asInstanceOf[Map[String, List[Map[String, Any]]]]

    m("languages") map {l => Language(l("name"), l("isActive"), l("completeness"))}
  }
  case None => Nil
}

languages foreach {println}

আমি প্রত্যাহারকারীদের এটি বের করতে পছন্দ করি।
ফিল

4
val jsonString =
  """
    |{
    | "languages": [{
    |     "name": "English",
    |     "is_active": true,
    |     "completeness": 2.5
    | }, {
    |     "name": "Latin",
    |     "is_active": false,
    |     "completeness": 0.9
    | }]
    |}
  """.stripMargin

val result = JSON.parseFull(jsonString).map {
  case json: Map[String, List[Map[String, Any]]] =>
    json("languages").map(l => (l("name"), l("is_active"), l("completeness")))
}.get

println(result)

assert( result == List(("English", true, 2.5), ("Latin", false, 0.9)) )

3
এটি সর্বশেষতম স্কেলে, আনব্যান্ডলড অবহিত। কোন ধারণা কীভাবে এটি ব্যবহার করবেন?
সংকেত_পটিল

4

আপনি এই মত করতে পারেন! JSON কোডটি পার্স করা খুব সহজ: পি

package org.sqkb.service.common.bean

import java.text.SimpleDateFormat

import org.json4s
import org.json4s.JValue
import org.json4s.jackson.JsonMethods._
//import org.sqkb.service.common.kit.{IsvCode}

import scala.util.Try

/**
  *
  */
case class Order(log: String) {

  implicit lazy val formats = org.json4s.DefaultFormats

  lazy val json: json4s.JValue = parse(log)

  lazy val create_time: String = (json \ "create_time").extractOrElse("1970-01-01 00:00:00")
  lazy val site_id: String = (json \ "site_id").extractOrElse("")
  lazy val alipay_total_price: Double = (json \ "alipay_total_price").extractOpt[String].filter(_.nonEmpty).getOrElse("0").toDouble
  lazy val gmv: Double = alipay_total_price
  lazy val pub_share_pre_fee: Double = (json \ "pub_share_pre_fee").extractOpt[String].filter(_.nonEmpty).getOrElse("0").toDouble
  lazy val profit: Double = pub_share_pre_fee

  lazy val trade_id: String = (json \ "trade_id").extractOrElse("")
  lazy val unid: Long = Try((json \ "unid").extractOpt[String].filter(_.nonEmpty).get.toLong).getOrElse(0L)
  lazy val cate_id1: Int = (json \ "cate_id").extractOrElse(0)
  lazy val cate_id2: Int = (json \ "subcate_id").extractOrElse(0)
  lazy val cate_id3: Int = (json \ "cate_id3").extractOrElse(0)
  lazy val cate_id4: Int = (json \ "cate_id4").extractOrElse(0)
  lazy val coupon_id: Long = (json \ "coupon_id").extractOrElse(0)

  lazy val platform: Option[String] = Order.siteMap.get(site_id)


  def time_fmt(fmt: String = "yyyy-MM-dd HH:mm:ss"): String = {
    val dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    val date = dateFormat.parse(this.create_time)
    new SimpleDateFormat(fmt).format(date)
  }

}

2

আমি স্ক্যাল পার্সার সংযুক্তকারী লাইব্রেরিটি এভাবেই করি:

import scala.util.parsing.combinator._
class ImprovedJsonParser extends JavaTokenParsers {

  def obj: Parser[Map[String, Any]] =
    "{" ~> repsep(member, ",") <~ "}" ^^ (Map() ++ _)

  def array: Parser[List[Any]] =
    "[" ~> repsep(value, ",") <~ "]"

  def member: Parser[(String, Any)] =
    stringLiteral ~ ":" ~ value ^^ { case name ~ ":" ~ value => (name, value) }

  def value: Parser[Any] = (
    obj
      | array
      | stringLiteral
      | floatingPointNumber ^^ (_.toDouble)
      |"true"
      |"false"
    )

}
object ImprovedJsonParserTest extends ImprovedJsonParser {
  def main(args: Array[String]) {
    val jsonString =
    """
      {
        "languages": [{
            "name": "English",
            "is_active": true,
            "completeness": 2.5
        }, {
            "name": "Latin",
            "is_active": false,
            "completeness": 0.9
        }]
      }
    """.stripMargin


    val result = parseAll(value, jsonString)
    println(result)

  }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.