8

次のjsonペイロードを想定

val json = """{
  "choices" : [
    {
      "name" : "A"
    },
    {
      "name" : "B"
    },
    {
      "name" : "C"
    },
    {
      "name" : "D"
    }
  ],
  "domain" : "Quizz",
  "level" : "Test",
  "mandatory": true
}"""

どのように変換しますか

val json = """{
  "value":"B",
  "domain" : "Quizz",
  "level" : "Test",
}"""

「B」は利用可能な選択肢からランダムに選択されますか?

これは私がこれまでに得たものです:

val cursor = parse(json).getOrElse(Json.Null).cursor
for{
  noMandatory<- cursor.downField("mandatory").flatMap(_.delete).map(_.top)
  withEmptyValue = noMandatory.deepMerge(Json.obj("value"->Json.Null))
 }yield withEmptyValue

これにより、未使用の「必須」フィールドが削除され、空の「値」フィールドが挿入されます。ただし、配列からランダムな値を取得して「値」に配置すると、私は逃げます。

- 編集

上記をより明確にするhcursorを使用してみました(とにかく私にとって)

val cursor = parse(json).getOrElse(Json.Null).hcursor

val noMandatory = cursor.downField("mandatory").delete
val withEmptyValue = noMandatory.withFocus(_.deepMerge(Json.obj("value"->Json.Null)))

(上記の例では circe 0.5.1 を使用しています)

4

3 に答える 3

3

純粋なコードに副作用を混ぜないでください。だから私はあなたのランダム化の問題を解決しませんでした.

第 2 に、単純にするために「choices」フィールドを削除しないことをお勧めします。必要に応じて、後でそのフィールドを削除します。

第 3 に、簡単にするために、取得段階と変換段階を分離します。ここでは、ケース クラスを使用してほとんどの作業を行うことができます。

これは慣用的な Circe ソリューションではないかもしれませんが、優れた Scala です。

import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._

object ChooserApp extends App {
  val input =
    """{
  "choices" : [
    {
      "name" : "A"
    },
    {
      "name" : "B"
    },
    {
      "name" : "C"
    },
    {
      "name" : "D"
    }
  ],
  "domain" : "Quizz",
  "level" : "Test",
  "mandatory": true
}"""

  val expected =
    """{
      "choices" : [
          {
            "name" : "A"
          },
          {
            "name" : "B"
          },
          {
            "name" : "C"
          },
          {
            "name" : "D"
          }
        ],
  "value":"B",
  "domain" : "Quizz",
  "level" : "Test",
  "mandatory": true
}"""

  case class ForJson(j: Json) {
    def choices: List[String] = {
      j.asObject
        .toList
        .flatMap(_ ("choices"))
        .flatMap(_.asArray)
        .flatten
        .flatMap(_.asObject)
        .flatMap(_ ("name"))
        .flatMap(_.asString)
    }

    def chosen(a: String): Json = {
      j.asObject
        .map(_.add("value", Json.fromString(a)))
        .map(Json.fromJsonObject)
        .getOrElse(j)
    }
  }

  val expectedList = List("A", "B", "C", "D")
  val gotList = ForJson(parse(input).toOption.get).choices
  assert(gotList == expectedList, s"Expected $expectedList, got $gotList")

  val expectedJson = parse(expected).toOption.get
  val gotJson = ForJson(parse(input).toOption.get).chosen("B")
  assert(gotJson == expectedJson, s"Expected $expectedJson, got $gotJson")
}
于 2016-09-26T14:47:36.890 に答える
1

APIを掘り下げた後、私が思いついたのは次のとおりです。

// a couple utilities
def renameKey(srcKey:String,target:String )(obj:JsonObject):Json= {
  obj("name").fold(obj)(value => obj.add("value", value).remove("name")).asJson
}
def safeDropKeys(json:Json)(keys:String*):Json=
  json.withObject(obj=>keys.foldLeft(obj)((acc, s)=>acc.remove(s)).asJson)

@scala-william のアドバイスに従って分離したランダム抽出器:

def extractRandomChip(cursor: HCursor): Xor[DecodingFailure, Json] = {
  val choice = cursor.get[Array[Json]]("choices").map { choices =>
    val x = util.Random.nextInt(choices.length)
    choices(x).withObject(renameKey("name", "value"))
  }
  choice
}

次に、すべてを接着します。

 val json = for {
  initial <- parse(s)
  value <- extractRandomChip(initial.hcursor)
  cleanedUp = safeDropKeys(initial)("mandatory","choices")
} yield cleanedUp.deepMerge(value)
json.map(_.toString).toOption

@wheaties の回答に関する私のコメントに従って、両方のオプションをベンチマークし、最速のオプションの結果を受け入れます。

于 2016-09-26T19:38:14.257 に答える