मानक स्काला कक्षाओं का उपयोग करके स्केल में JSON को पार्स करने के लिए कैसे?


113

मैं JSON कोड को पार्स करने के लिए Scala 2.8 में JSON क्लास में बिल्ड का उपयोग कर रहा हूं। मैं निर्भरता कम करने के कारण लिफ़्टवेब एक या किसी अन्य का उपयोग नहीं करना चाहता।

जिस तरह से मैं कर रहा हूं वह बहुत जरूरी है, क्या इसे करने का एक बेहतर तरीका है?

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)))

लूप की शुरुआत में मैं कृत्रिम रूप से एक सूची में परिणाम को लपेटता हूं ताकि यह अंत में एक सूची उत्पन्न करे। फिर बाकी लूप के लिए मैं इस तथ्य का उपयोग करता हूं कि जनरेटर (उपयोग <-) और मूल्य परिभाषाएं (उपयोग करते हुए)= ) अनपेक्षित तरीकों का उपयोग करेंगे।

(पुराना उत्तर संपादित किया गया - यदि आप उत्सुक हैं तो इतिहास संपादित करें की जाँच करें)


एक पुरानी पोस्ट को खोदने के लिए क्षमा करें, लेकिन लूप में पहले कुछ (एम (मानचित्र)) के लिए क्या अर्थ है? मैं समझता हूं कि एम (मैप) नक्शे को चर "मैप" में निकाल रहा है, लेकिन कुछ के बारे में क्या?
फेडेरिको बोनेली

1
@FedericoBonelli, JSON.parseFullरिटर्न देता है Option[Any], इसलिए यह List(None)या के साथ शुरू होता है List(Some(any))Someपर पैटर्न मिलान के लिए है Option
हुहुंजल

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 का एक उदाहरण दे सकते हैं, यह jsonStr के उपरोक्त उदाहरण के साथ काम नहीं कर रहा है
priya khokher

यह आपके मुद्दे के बारे में अपना प्रश्न पोस्ट करने के लायक हो सकता है। वर्तमान में मैंने अपनी मशीन पर Scala स्थापित नहीं किया है, इसलिए मेरे पास JSON स्ट्रिंग तैयार नहीं है।
मथायस ब्रौन

12

मुझे @ huynhjl का जवाब पसंद है, इसने मुझे सही रास्ते पर ला दिया। हालाँकि, यह त्रुटि स्थितियों से निपटने में बहुत अच्छा नहीं है। यदि वांछित नोड मौजूद नहीं है, तो आपको एक कास्ट अपवाद मिलता है। मैंने इसे 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 डेटा की संरचना को प्रतिबिंबित करता है और पूरी तरह से राज्य के लिए या तो बोझिल या असंभव है। मुझे लगता है कि क्यों किसी भी प्रकार की परिभाषाओं को कम करने के लिए उपयोग किया जाता है। किसी का उपयोग करना करने से कास्टिंग की आवश्यकता होती है।

मैंने नीचे कुछ हैक किया है जो संक्षिप्त है लेकिन प्रश्न में कोड द्वारा निहित 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
यह नवीनतम स्केला, Unbundled में चित्रित किया गया है। किसी भी विचार यह कैसे उपयोग करने के लिए?
साकेत_पेटिल

4

आप ऐसा कर सकते हैं! JSON कोड पार्स करना बहुत आसान है: P

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)

  }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.