11

私はJson4sライブラリを学んでいます。

次のようなjsonフラグメントがあります。

{
    "records":[
        {
            "name":"John Derp",
            "address":"Jem Street 21"
        },
        {
            "name":"Scala Jo",
            "address":"in my sweet dream"
        }
    ]
}

そして、次のように、json 文字列をマップのリストに変換する Scala コードがあります。

import org.json4s._
import org.json4s.JsonAST._
import org.json4s.native.JsonParser

  val json = JsonParser.parse( """{"records":[{"name":"John Derp","address":"Jem Street 21"},{"name":"Scala Jo","address":"in my sweet dream"}]}""")

  val records: List[Map[String, Any]] = for {
    JObject(rec) <- json \ "records"
    JField("name", JString(name)) <- rec
    JField("address", JString(address)) <- rec
  } yield Map("name" -> name, "address" -> address)

  println(records)

to screenの出力は次のrecordsようになります。

List(Map(name -> John Derp, address -> Jem Street 21), Map(name -> Scala Jo, address -> in my sweet dream))

forループ内の行の意味を理解したい。たとえば、次の行の意味は何ですか。

JObject(rec) <- json \ "records"

json \ "records"がオブジェクトを生成することは理解していますが、 の左側のJArrayようにフェッチされるのはなぜですか? 構文の意味は何ですか? 変数はどこから来ますか?入力から新しいクラスをインスタンス化するということですか?JObject(rec)<-JObject(rec)recJObject(rec)JObjectrec

ところで、私は Java プログラミングの経験があるので、上記のループに相当する Java コードを教えていただけると助かります。

4

3 に答える 3

7

次のタイプの階層があります。

  sealed abstract class JValue {
    def \(nameToFind: String): JValue = ???
    def filter(p: (JValue) => Boolean): List[JValue] = ???
  }

  case class JObject(val obj: List[JField]) extends JValue
  case class JField(val name: String, val value: JValue) extends JValue
  case class JString(val s: String) extends JValue
  case class JArray(val arr: List[JValue]) extends JValue {
    override def filter(p: (JValue) => Boolean): List[JValue] = 
      arr.filter(p)
  }

JSON パーサーは次のオブジェクトを返します。

  object JsonParser {
    def parse(s: String): JValue = {
      new JValue {
        override def \(nameToFind: String): JValue =
          JArray(List(
            JObject(List(
              JField("name", JString("John Derp")),
              JField("address", JString("Jem Street 21")))),
            JObject(List(
              JField("name", JString("Scala Jo")),
              JField("address", JString("in my sweet dream"))))))
      }
    }
  }

  val json = JsonParser.parse("Your JSON")

内部では、Scala コンパイラは以下を生成します。

  val res = (json \ "records")
    .filter(_.isInstanceOf[JObject])
    .flatMap { x =>
      x match {
        case JObject(obj) => //
          obj //
            .withFilter(f => f match {
              case JField("name", _) => true
              case _                 => false
            }) //
            .flatMap(n => obj.withFilter(f => f match {
              case JField("address", _) => true
              case _                    => false
            }).map(a => Map(
              "name" -> (n.value match { case JString(name) => name }),
              "address" -> (a.value match { case JString(address) => address }))))
      }
    }

最初の行は(つまり) を返すJObject(rec) <- json \ "records"ため可能です。ここで、 の各値はパターン マッチングを使用してにマップされます。JArray.filterList[JValue]List[JObject]List[JValue]JObject(rec)

残りの呼び出しは一連の flatMap と map (これが Scala for Comprehensions の仕組みです) とパターン マッチングです。

Scala 2.11.4 を使用しました。

もちろん、match上記の式は一連の型チェックとキャストを使用して実装されています。

アップデート:

ライブラリを使用すると、 からへJson4sの暗黙的な変換が行われます。参照:JValueorg.json4s.MonadicJValuepackage object json4s

implicit def jvalue2monadic(jv: JValue) = new MonadicJValue(jv)

この変換はここで使用されます: JObject(rec) <- json \ "records". 最初にjsonが に変換されMonadicJValue、次にdef \("records")が適用def filterされ、その結果が でdef \あるJValueに使用されます。次に、再び暗黙的に に変換されMonadicJValue、次にdef filterMonadicJValue使用されます。の結果はMonadicJValue.filterですList[JValue]。その後、上述のステップが実行される。

于 2015-01-09T14:05:05.627 に答える
5

あなたは理解のために Scala を使用していますが、混乱の多くは理解がどのように機能するかについてだと思います。これは、モナドの map メソッド、 flatMap メソッド、および filter メソッドにアクセスしてコレクションを反復処理する簡潔な方法の Scala 構文です。これを完全に理解するには、モナドと理解についてある程度の理解が必要です。Scalaのドキュメントが役に立ちます。「scala for Comprehension」の検索も役立ちます。また、Scala のエクストラクターについても理解する必要があります。

あなたはこの行の意味について尋ねました:

JObject(rec) <- json \ "records"

これは理解のための一部です。

あなたの声明:

json\"records" が JArray オブジェクトを生成することは理解していますが、

は少し間違っています。\ 関数List[JSObject]は、パーサーの結果からa を抽出します。json

しかし、なぜ <- の左側で JObject(rec) としてフェッチされるのですか?

json \ "records"json4s エクストラクタ \ を使用して、Json データの「レコード」メンバーを選択し、List[JObject]. は<-「取得元」と読むことができ、リストを繰り返し処理していることを意味します。リストの要素には JObject 型があり、構造体はエクストラクタを適用して、JObject (そのフィールド) のコンテンツを保持JObject(rec)する値 を作成します。rec

<- の左側にある JObject(rec) としてフェッチされるのはなぜですか?

これは、コレクションを反復処理するための Scala 構文です。たとえば、次のように書くこともできます。

for (x <- 1 to 10)

これは単に 1 から 10 の値を与えるだけですx。あなたの例では、同様の種類の反復を使用していますが、JObjects のリストの内容に対して使用しています。

JObject(rec) の意味は何ですか?

これは Scala エクストラクターです。json4s コードを見ると、JObject が次のように定義されていることがわかります。

case class JObject(obj: List[JField]) extends JValue

Scala にケース クラスがある場合、apply と unapply の 2 つのメソッドが自動的に定義されます。thenの意味はJObject(rec)、una​​pply メソッドを呼び出して、JObject コンストラクター (apply メソッド)recの値に対応する値 を生成することです。objしたがって、recタイプは になりList[JField]ます。

rec 変数はどこから来たのですか?

obj単純に使用するだけで、 JObject の apply メソッドへのパラメーターのプレースホルダーとして宣言されます。

JObject(rec) は rec 入力から新しい JObject クラスをインスタンス化することを意味しますか?

いいえ、そうではありません。これは、結果のjson \ "records"JArray に JObject 値のみが含まれているために発生します。

したがって、これを解釈するには:

JObject(rec) <- json \ "records"

次の疑似コードを英語で書くことができます。

解析された json 内の「レコード」を JArray として検索し、それらを反復処理します。JArray の要素は JObject 型である必要があります。各 JObject の「obj」フィールドを JField のリストとして取得し、「rec」という名前の値に割り当てます。

うまくいけば、これですべてが少し明確になりますか?

また、上記のループに相当する Java コードを示していただけると助かります。

もちろんそれも可能ですが、私がここで貢献したいことよりもはるかに多くの作業が必要です。できることの 1 つは、コードを Scala でコンパイルし、関連する .class ファイルを見つけて、それらを Java として逆コンパイルすることです。これは、Scala が Java よりもプログラミングをどれだけ簡素化するかを学ぶ上で非常に有益かもしれません。:)

なぜ私はこれを行うことができないのですか?for ( rec <- json \ "records" なので、 rec は JObject になります。 <- の左側にある JObject(rec) の理由は何ですか?

あなたは出来る!ただし、JObject のコンテンツを取得する必要があります。このように理解のために書くことができます:

val records: List[Map[String, Any]] = for {
    obj: JObject <- json \ "records"
    rec = obj.obj
    JField("name", JString(name)) <- rec
    JField("address", JString(address)) <- rec
  } yield Map("name" -> name, "address" -> address)

同じ意味ですが、長くなります。

以前は for (x <- y パターンしか見たことがないので、N(x) パターンの意味を理解したいだけです。

上で説明したように、これは、ケース クラスに対して自動的に作成される unapply メソッドを使用する単純なエクストラクタです。同様のことが Scala の case ステートメントで行われます。

更新: あなたが提供したコードは、json4s-native の 3.2.11 バージョンに対してコンパイルされません。このインポート:

import org.json4s.JsonAST._

このインポートでは冗長です:

import org.json4s._

JObject が 2 回定義されます。JsonAST インポートを削除すると、正常にコンパイルされます。

これをもう少しテストするために、コードを次のように scala ファイルに入れます。

package example

import org.json4s._
// import org.json4s.JsonAST._
import org.json4s.native.JsonParser

class ForComprehension {
  val json = JsonParser.parse(
    """{
      |"records":[
      |{"name":"John Derp","address":"Jem Street 21"},
      |{"name":"Scala Jo","address":"in my sweet dream"}
      |]}""".stripMargin
  )

  val records: List[Map[String, Any]] = for {
    JObject(rec) <- json \ "records"
    JField("name", JString(name)) <- rec
    JField("address", JString(address)) <- rec
  } yield Map("name" -> name, "address" -> address)

  println(records)
}

次に、Scala REPL セッションを開始して調査しました。

scala> import example.ForComprehension
import example.ForComprehension

scala> val x = new ForComprehension
List(Map(name -> John Derp, address -> Jem Street 21), Map(name -> Scala Jo, address -> in my sweet dream))
x: example.ForComprehension = example.ForComprehension@5f9cbb71

scala> val obj = x.json \ "records"
obj: org.json4s.JValue = JArray(List(JObject(List((name,JString(John Derp)), (address,JString(Jem Street 21)))), JObject(List((name,JString(Scala Jo)), (address,JString(in my sweet dream))))))

scala> for (a <- obj) yield { a }
res1: org.json4s.JValue = JArray(List(JObject(List((name,JString(John Derp)), (address,JString(Jem Street 21)))), JObject(List((name,JString(Scala Jo)), (address,JString(in my sweet dream))))))

scala> import org.json4s.JsonAST.JObject
for ( JObject(rec) <- obj ) yield { rec }
import org.json4s.JsonAST.JObject

scala> res2: List[List[org.json4s.JsonAST.JField]] = List(List((name,JString(John Derp)), (address,JString(Jem Street 21))), List((name,JString(Scala Jo)), (address,JString(in my sweet dream))))

そう:

  • \ 演算子の結果は JArray です。
  • JArray に対する「反復」は、配列全体をリスト内の唯一の値として扱います。
  • JArray から JObject への暗黙的な変換が必要であり、エクストラクタが JArray の内容を List[JField] として生成できるようにする必要があります。
  • すべてがリストになると、for 内包表記は通常どおりに処理されます。

これを理解するのに役立つことを願っています。

割り当て内のパターン マッチングの詳細については、このブログをお試しください

更新 #2 :ここで行われている暗黙の変換を発見するために、もう少し掘り下げました。犯人は \ 演算子です。モナドの反復可能なものになる方法を理解するjson \ "records"には、次のコードを確認する必要があります。

  • org.json4s package objectJValue : この行は、 からへの暗黙的な変換を宣言しMonadicJValueます。だから何MonadicJValueですか?
  • org.json4s.MonadicJValue : これは、JValues を for 内包表記で反復可能にするすべてのものを定義します: フィルター、マップ、フラットマップ、および \ および \\ XPath のような演算子も提供します。

したがって、本質的に、\ 演算子を使用すると、次の一連のアクションが実行されます: - json (JValue) を MonadicJValue に暗黙的に変換します - MonadicJValue で \ 演算子を適用して、JArray (「レコード」) を生成します - JArray を暗黙的に変換しますMonadicJValue へ - MonadicJValue.filter および MonadicJValue.map メソッドを使用して、理解のために実装します

于 2015-01-09T14:24:23.623 に答える